Blame kpatch-build/create-diff-object.c

Packit Service ac8aad
/*
Packit Service ac8aad
 * create-diff-object.c
Packit Service ac8aad
 *
Packit Service ac8aad
 * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
Packit Service ac8aad
 * Copyright (C) 2013-2014 Josh Poimboeuf <jpoimboe@redhat.com>
Packit Service ac8aad
 *
Packit Service ac8aad
 * This program is free software; you can redistribute it and/or
Packit Service ac8aad
 * modify it under the terms of the GNU General Public License
Packit Service ac8aad
 * as published by the Free Software Foundation; either version 2
Packit Service ac8aad
 * of the License, or (at your option) any later version.
Packit Service ac8aad
 *
Packit Service ac8aad
 * This program is distributed in the hope that it will be useful,
Packit Service ac8aad
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service ac8aad
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service ac8aad
 * GNU General Public License for more details.
Packit Service ac8aad
 *
Packit Service ac8aad
 * You should have received a copy of the GNU General Public License
Packit Service ac8aad
 * along with this program; if not, write to the Free Software
Packit Service ac8aad
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
Packit Service ac8aad
 * 02110-1301, USA.
Packit Service ac8aad
 */
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * This file contains the heart of the ELF object differencing engine.
Packit Service ac8aad
 *
Packit Service ac8aad
 * The tool takes two ELF objects from two versions of the same source
Packit Service ac8aad
 * file; a "base" object and a "patched" object.  These object need to have
Packit Service ac8aad
 * been compiled with the -ffunction-sections and -fdata-sections GCC options.
Packit Service ac8aad
 *
Packit Service ac8aad
 * The tool compares the objects at a section level to determine what
Packit Service ac8aad
 * sections have changed.  Once a list of changed sections has been generated,
Packit Service ac8aad
 * various rules are applied to determine any object local sections that
Packit Service ac8aad
 * are dependencies of the changed section and also need to be included in
Packit Service ac8aad
 * the output object.
Packit Service ac8aad
 */
Packit Service ac8aad
Packit Service ac8aad
#include <sys/types.h>
Packit Service ac8aad
#include <sys/stat.h>
Packit Service ac8aad
#include <fcntl.h>
Packit Service ac8aad
#include <stdlib.h>
Packit Service ac8aad
#include <stdio.h>
Packit Service ac8aad
#include <string.h>
Packit Service ac8aad
#include <error.h>
Packit Service ac8aad
#include <gelf.h>
Packit Service ac8aad
#include <argp.h>
Packit Service ac8aad
#include <libgen.h>
Packit Service ac8aad
#include <unistd.h>
Packit Service ac8aad
Packit Service ac8aad
#include "list.h"
Packit Service ac8aad
#include "lookup.h"
Packit Service ac8aad
#include "asm/insn.h"
Packit Service ac8aad
#include "kpatch-patch.h"
Packit Service ac8aad
#include "kpatch-elf.h"
Packit Service ac8aad
#include "kpatch-intermediate.h"
Packit Service ac8aad
#include "kpatch.h"
Packit Service ac8aad
Packit Service ac8aad
#define DIFF_FATAL(format, ...) \
Packit Service ac8aad
({ \
Packit Service ac8aad
	fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
Packit Service ac8aad
	error(EXIT_STATUS_DIFF_FATAL, 0, "unreconcilable difference"); \
Packit Service ac8aad
})
Packit Service ac8aad
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
#define ABSOLUTE_RELA_TYPE R_PPC64_ADDR64
Packit Service ac8aad
#else
Packit Service ac8aad
#define ABSOLUTE_RELA_TYPE R_X86_64_64
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
char *childobj;
Packit Service ac8aad
Packit Service ac8aad
enum loglevel loglevel = NORMAL;
Packit Service ac8aad
Packit Service ac8aad
/*******************
Packit Service ac8aad
 * Data structures
Packit Service ac8aad
 * ****************/
Packit Service ac8aad
struct special_section {
Packit Service ac8aad
	char *name;
Packit Service ac8aad
	int (*group_size)(struct kpatch_elf *kelf, int offset);
Packit Service ac8aad
};
Packit Service ac8aad
Packit Service ac8aad
/*************
Packit Service ac8aad
 * Functions
Packit Service ac8aad
 * **********/
Packit Service ac8aad
Packit Service ac8aad
static int is_bundleable(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	if (sym->type == STT_FUNC &&
Packit Service ac8aad
	    !strncmp(sym->sec->name, ".text.",6) &&
Packit Service ac8aad
	    !strcmp(sym->sec->name + 6, sym->name))
Packit Service ac8aad
		return 1;
Packit Service ac8aad
Packit Service ac8aad
	if (sym->type == STT_FUNC &&
Packit Service ac8aad
	    !strncmp(sym->sec->name, ".text.unlikely.",15) &&
Packit Service ac8aad
	    (!strcmp(sym->sec->name + 15, sym->name) ||
Packit Service ac8aad
			 (strstr(sym->name, ".cold.") &&
Packit Service ac8aad
			  !strncmp(sym->sec->name + 15, sym->name, strlen(sym->sec->name) - 15))))
Packit Service ac8aad
		return 1;
Packit Service ac8aad
Packit Service ac8aad
	if (sym->type == STT_OBJECT &&
Packit Service ac8aad
	   !strncmp(sym->sec->name, ".data.",6) &&
Packit Service ac8aad
	   !strcmp(sym->sec->name + 6, sym->name))
Packit Service ac8aad
		return 1;
Packit Service ac8aad
Packit Service ac8aad
	if (sym->type == STT_OBJECT &&
Packit Service ac8aad
	   !strncmp(sym->sec->name, ".rodata.",8) &&
Packit Service ac8aad
	   !strcmp(sym->sec->name + 8, sym->name))
Packit Service ac8aad
		return 1;
Packit Service ac8aad
Packit Service ac8aad
	if (sym->type == STT_OBJECT &&
Packit Service ac8aad
	   !strncmp(sym->sec->name, ".bss.",5) &&
Packit Service ac8aad
	   !strcmp(sym->sec->name + 5, sym->name))
Packit Service ac8aad
		return 1;
Packit Service ac8aad
Packit Service ac8aad
	return 0;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
/* Symbol st_others value for powerpc */
Packit Service ac8aad
#define STO_PPC64_LOCAL_BIT     5
Packit Service ac8aad
#define STO_PPC64_LOCAL_MASK    (7 << STO_PPC64_LOCAL_BIT)
Packit Service ac8aad
#define PPC64_LOCAL_ENTRY_OFFSET(other)                                 \
Packit Service ac8aad
	        (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * On ppc64le, the function prologue generated by GCC 6+ has the sequence:
Packit Service ac8aad
 *
Packit Service ac8aad
 *	.globl my_func
Packit Service ac8aad
 *	.type my_func, @function
Packit Service ac8aad
 *	.quad .TOC.-my_func
Packit Service ac8aad
 * my_func:
Packit Service ac8aad
 *	.reloc ., R_PPC64_ENTRY ; optional
Packit Service ac8aad
 *	ld r2,-8(r12)
Packit Service ac8aad
 *	add r2,r2,r12
Packit Service ac8aad
 *	.localentry my_func, .-my_func
Packit Service ac8aad
 *
Packit Service ac8aad
 * my_func is the global entry point, which, when called, sets up the TOC.
Packit Service ac8aad
 * .localentry is the local entry point, for calls to the function from within
Packit Service ac8aad
 * the object file.  The local entry point is 8 bytes after the global entry
Packit Service ac8aad
 * point.
Packit Service ac8aad
 */
Packit Service ac8aad
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	return ((PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other) != 0) &&
Packit Service ac8aad
		sym->sym.st_value == 8);
Packit Service ac8aad
}
Packit Service ac8aad
#else
Packit Service ac8aad
static int is_gcc6_localentry_bundled_sym(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	return 0;
Packit Service ac8aad
}
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
static struct rela *toc_rela(const struct rela *rela)
Packit Service ac8aad
{
Packit Service ac8aad
	if (rela->type != R_PPC64_TOC16_HA &&
Packit Service ac8aad
	    rela->type != R_PPC64_TOC16_LO_DS)
Packit Service ac8aad
		return (struct rela *)rela;
Packit Service ac8aad
Packit Service ac8aad
	/* Will return NULL for .toc constant entries */
Packit Service ac8aad
	return find_rela_by_offset(rela->sym->sec->rela, rela->addend);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * When compiling with -ffunction-sections and -fdata-sections, almost every
Packit Service ac8aad
 * symbol gets its own dedicated section.  We call such symbols "bundled"
Packit Service ac8aad
 * symbols.  They're indicated by "sym->sec->sym == sym".
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_bundle_symbols(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (is_bundleable(sym)) {
Packit Service ac8aad
			if (sym->sym.st_value != 0 &&
Packit Service ac8aad
			    !is_gcc6_localentry_bundled_sym(sym)) {
Packit Service ac8aad
				ERROR("symbol %s at offset %lu within section %s, expected 0",
Packit Service ac8aad
				      sym->name, sym->sym.st_value,
Packit Service ac8aad
				      sym->sec->name);
Packit Service ac8aad
			}
Packit Service ac8aad
Packit Service ac8aad
			sym->sec->sym = sym;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * This function detects whether the given symbol is a "special" static local
Packit Service ac8aad
 * variable (for lack of a better term).
Packit Service ac8aad
 *
Packit Service ac8aad
 * Special static local variables should never be correlated and should always
Packit Service ac8aad
 * be included if they are referenced by an included function.
Packit Service ac8aad
 */
Packit Service ac8aad
static int is_special_static(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	static char *prefixes[] = {
Packit Service ac8aad
		"__key.",
Packit Service ac8aad
		"__warned.",
Packit Service ac8aad
		"descriptor.",
Packit Service ac8aad
		"__func__.",
Packit Service ac8aad
		"_rs.",
Packit Service ac8aad
		"CSWTCH.",
Packit Service ac8aad
		NULL,
Packit Service ac8aad
	};
Packit Service ac8aad
	char **prefix;
Packit Service ac8aad
Packit Service ac8aad
	if (!sym)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	if (sym->type == STT_SECTION) {
Packit Service ac8aad
		/* __verbose section contains the descriptor variables */
Packit Service ac8aad
		if (!strcmp(sym->name, "__verbose"))
Packit Service ac8aad
			return 1;
Packit Service ac8aad
Packit Service ac8aad
		/* otherwise make sure section is bundled */
Packit Service ac8aad
		if (!sym->sec->sym)
Packit Service ac8aad
			return 0;
Packit Service ac8aad
Packit Service ac8aad
		/* use bundled object/function symbol for matching */
Packit Service ac8aad
		sym = sym->sec->sym;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	for (prefix = prefixes; *prefix; prefix++)
Packit Service ac8aad
		if (!strncmp(sym->name, *prefix, strlen(*prefix)))
Packit Service ac8aad
			return 1;
Packit Service ac8aad
Packit Service ac8aad
	return 0;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * This is like strcmp, but for gcc-mangled symbols.  It skips the comparison
Packit Service ac8aad
 * of any substring which consists of '.' followed by any number of digits.
Packit Service ac8aad
 */
Packit Service ac8aad
static int kpatch_mangled_strcmp(char *s1, char *s2)
Packit Service ac8aad
{
Packit Service ac8aad
	while (*s1 == *s2) {
Packit Service ac8aad
		if (!*s1)
Packit Service ac8aad
			return 0;
Packit Service ac8aad
		if (*s1 == '.' && isdigit(s1[1])) {
Packit Service ac8aad
			if (!isdigit(s2[1]))
Packit Service ac8aad
				return 1;
Packit Service ac8aad
			while (isdigit(*++s1))
Packit Service ac8aad
				;
Packit Service ac8aad
			while (isdigit(*++s2))
Packit Service ac8aad
				;
Packit Service ac8aad
		} else {
Packit Service ac8aad
			s1++;
Packit Service ac8aad
			s2++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
	return 1;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int rela_equal(struct rela *rela1, struct rela *rela2)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela_toc1, *rela_toc2;
Packit Service ac8aad
	unsigned long toc_data1 = 0, toc_data2 = 0; /* = 0 to prevent gcc warning */
Packit Service ac8aad
Packit Service ac8aad
	if (rela1->type != rela2->type ||
Packit Service ac8aad
	    rela1->offset != rela2->offset)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * With -mcmodel=large on ppc64le, GCC might generate entries in the .toc
Packit Service ac8aad
	 * section for relocation symbol references.   The .toc offsets may change
Packit Service ac8aad
	 * between the original and patched .o, so comparing ".toc + offset" isn't
Packit Service ac8aad
	 * right.  Compare the .toc-based symbols by reading the corresponding relas
Packit Service ac8aad
	 * from the .toc section.
Packit Service ac8aad
	 */
Packit Service ac8aad
	rela_toc1 = toc_rela(rela1);
Packit Service ac8aad
	if (!rela_toc1) {
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * .toc section entries are mostly place holder for relocation entries, specified
Packit Service ac8aad
		 * in .rela.toc section. Sometimes, .toc section may have constants as entries.
Packit Service ac8aad
		 * These constants are not reference to any symbols, but plain instructions mostly
Packit Service ac8aad
		 * due to some arithmetics in the functions referring them.
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * They are referred by the functions like normal .toc entries, these entries can
Packit Service ac8aad
		 * not be resolved to any symbols.
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * Disassembly of section .toc:
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * 0000000000000000 <.toc>:
Packit Service ac8aad
		 *         ...
Packit Service ac8aad
		 *                        148: R_PPC64_ADDR64     .data.capacity_margin
Packit Service ac8aad
		 *  150:   0b d7 a3 70     andi.   r3,r5,55051
Packit Service ac8aad
		 *  154:   3d 0a d7 a3     lhz     r30,2621(r23)
Packit Service ac8aad
		 *                        158: R_PPC64_ADDR64     sched_max_numa_distance
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * Relocation section '.rela.toc' at offset 0xadac0 contains 160 entries:
Packit Service ac8aad
		 *     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
Packit Service ac8aad
		 *     ...
Packit Service ac8aad
		 * 0000000000000148  0000009100000026 R_PPC64_ADDR64         0000000000000000 .data.capacity_margin + 0
Packit Service ac8aad
		 * 0000000000000158  000001a500000026 R_PPC64_ADDR64         0000000000000000 sched_max_numa_distance + 0
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * Relocation section '.rela.text.select_task_rq_fair' at offset 0x90e98 contains 37 entries:
Packit Service ac8aad
		 *     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
Packit Service ac8aad
		 *     ...
Packit Service ac8aad
		 * 00000000000004a0  0000008800000032 R_PPC64_TOC16_HA       0000000000000000 .toc + 148
Packit Service ac8aad
		 * 00000000000004ac  0000008800000040 R_PPC64_TOC16_LO_DS    0000000000000000 .toc + 148
Packit Service ac8aad
		 * 0000000000000514  0000008800000032 R_PPC64_TOC16_HA       0000000000000000 .toc + 150
Packit Service ac8aad
		 * 000000000000051c  0000008800000040 R_PPC64_TOC16_LO_DS    0000000000000000 .toc + 150
Packit Service ac8aad
		 */
Packit Service ac8aad
		memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1));
Packit Service ac8aad
		if (!toc_data1)
Packit Service ac8aad
			ERROR(".toc entry not found %s + %x", rela1->sym->name, rela1->addend);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	rela_toc2 = toc_rela(rela2);
Packit Service ac8aad
	if (!rela_toc2) {
Packit Service ac8aad
		memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2));
Packit Service ac8aad
		if (!toc_data2)
Packit Service ac8aad
			ERROR(".toc entry not found %s + %x", rela2->sym->name, rela2->addend);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!rela_toc1 && !rela_toc2)
Packit Service ac8aad
		return toc_data1 == toc_data2;
Packit Service ac8aad
Packit Service ac8aad
	if (rela_toc1->string)
Packit Service ac8aad
		return rela_toc2->string && !strcmp(rela_toc1->string, rela_toc2->string);
Packit Service ac8aad
Packit Service ac8aad
	if (rela_toc1->addend != rela_toc2->addend)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	if (is_special_static(rela_toc1->sym))
Packit Service ac8aad
		return !kpatch_mangled_strcmp(rela_toc1->sym->name,
Packit Service ac8aad
					      rela_toc2->sym->name);
Packit Service ac8aad
Packit Service ac8aad
	return !strcmp(rela_toc1->sym->name, rela_toc2->sym->name);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_correlated_rela_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela1, *rela2 = NULL;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * On ppc64le, don't compare the .rela.toc section.  The .toc and
Packit Service ac8aad
	 * .rela.toc sections are included as standard elements.
Packit Service ac8aad
	 */
Packit Service ac8aad
	if (!strcmp(sec->name, ".rela.toc")) {
Packit Service ac8aad
		sec->status = SAME;
Packit Service ac8aad
		return;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	rela2 = list_entry(sec->twin->relas.next, struct rela, list);
Packit Service ac8aad
	list_for_each_entry(rela1, &sec->relas, list) {
Packit Service ac8aad
		if (rela_equal(rela1, rela2)) {
Packit Service ac8aad
			rela2 = list_entry(rela2->list.next, struct rela, list);
Packit Service ac8aad
			continue;
Packit Service ac8aad
		}
Packit Service ac8aad
		sec->status = CHANGED;
Packit Service ac8aad
		return;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	sec->status = SAME;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_correlated_nonrela_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec1 = sec, *sec2 = sec->twin;
Packit Service ac8aad
Packit Service ac8aad
	if (sec1->sh.sh_type != SHT_NOBITS &&
Packit Service ac8aad
	    memcmp(sec1->data->d_buf, sec2->data->d_buf, sec1->data->d_size))
Packit Service ac8aad
		sec->status = CHANGED;
Packit Service ac8aad
	else
Packit Service ac8aad
		sec->status = SAME;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_correlated_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec1 = sec, *sec2 = sec->twin;
Packit Service ac8aad
Packit Service ac8aad
	/* Compare section headers (must match or fatal) */
Packit Service ac8aad
	if (sec1->sh.sh_type != sec2->sh.sh_type ||
Packit Service ac8aad
	    sec1->sh.sh_flags != sec2->sh.sh_flags ||
Packit Service ac8aad
	    sec1->sh.sh_addralign != sec2->sh.sh_addralign ||
Packit Service ac8aad
	    sec1->sh.sh_entsize != sec2->sh.sh_entsize)
Packit Service ac8aad
		DIFF_FATAL("%s section header details differ", sec1->name);
Packit Service ac8aad
Packit Service ac8aad
	/* Short circuit for mcount sections, we rebuild regardless */
Packit Service ac8aad
	if (!strcmp(sec->name, ".rela__mcount_loc") ||
Packit Service ac8aad
	    !strcmp(sec->name, "__mcount_loc")) {
Packit Service ac8aad
		sec->status = SAME;
Packit Service ac8aad
		goto out;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (sec1->sh.sh_size != sec2->sh.sh_size ||
Packit Service ac8aad
	    sec1->data->d_size != sec2->data->d_size) {
Packit Service ac8aad
		sec->status = CHANGED;
Packit Service ac8aad
		goto out;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (is_rela_section(sec))
Packit Service ac8aad
		kpatch_compare_correlated_rela_section(sec);
Packit Service ac8aad
	else
Packit Service ac8aad
		kpatch_compare_correlated_nonrela_section(sec);
Packit Service ac8aad
out:
Packit Service ac8aad
	if (sec->status == CHANGED)
Packit Service ac8aad
		log_debug("section %s has changed\n", sec->name);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
#ifdef __x86_64__
Packit Service ac8aad
/*
Packit Service ac8aad
 * Determine if a section has changed only due to a WARN* or might_sleep
Packit Service ac8aad
 * macro call's embedding of the line number into an instruction operand.
Packit Service ac8aad
 *
Packit Service ac8aad
 * Warning: Hackery lies herein.  It's hopefully justified by the fact that
Packit Service ac8aad
 * this issue is very common.
Packit Service ac8aad
 *
Packit Service ac8aad
 * Example WARN*:
Packit Service ac8aad
 *
Packit Service ac8aad
 *  938:   be 70 00 00 00          mov    $0x70,%esi
Packit Service ac8aad
 *  93d:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi
Packit Service ac8aad
 *                         940: R_X86_64_32S       .rodata.tcp_conn_request.str1.8+0x88
Packit Service ac8aad
 *  944:   c6 05 00 00 00 00 01    movb   $0x1,0x0(%rip)        # 94b <tcp_conn_request+0x94b>
Packit Service ac8aad
 *                         946: R_X86_64_PC32      .data.unlikely-0x1
Packit Service ac8aad
 *  94b:   e8 00 00 00 00          callq  950 <tcp_conn_request+0x950>
Packit Service ac8aad
 *                         94c: R_X86_64_PC32      warn_slowpath_null-0x4
Packit Service ac8aad
 *
Packit Service ac8aad
 * Example might_sleep:
Packit Service ac8aad
 *
Packit Service ac8aad
 *  50f:   be f7 01 00 00          mov    $0x1f7,%esi
Packit Service ac8aad
 *  514:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi
Packit Service ac8aad
 *                         517: R_X86_64_32S       .rodata.do_select.str1.8+0x98
Packit Service ac8aad
 *  51b:   e8 00 00 00 00          callq  520 <do_select+0x520>
Packit Service ac8aad
 *                         51c: R_X86_64_PC32      ___might_sleep-0x4
Packit Service ac8aad
 *
Packit Service ac8aad
 * The pattern which applies to all cases:
Packit Service ac8aad
 * 1) immediate move of the line number to %esi
Packit Service ac8aad
 * 2) (optional) string section rela
Packit Service ac8aad
 * 3) (optional) __warned.xxxxx static local rela
Packit Service ac8aad
 * 4) warn_slowpath_* or __might_sleep or some other similar rela
Packit Service ac8aad
 */
Packit Service ac8aad
static int kpatch_line_macro_change_only(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct insn insn1, insn2;
Packit Service ac8aad
	unsigned long start1, start2, size, offset, length;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int lineonly = 0, found;
Packit Service ac8aad
Packit Service ac8aad
	if (sec->status != CHANGED ||
Packit Service ac8aad
	    is_rela_section(sec) ||
Packit Service ac8aad
	    !is_text_section(sec) ||
Packit Service ac8aad
	    sec->sh.sh_size != sec->twin->sh.sh_size ||
Packit Service ac8aad
	    !sec->rela ||
Packit Service ac8aad
	    sec->rela->status != SAME)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	start1 = (unsigned long)sec->twin->data->d_buf;
Packit Service ac8aad
	start2 = (unsigned long)sec->data->d_buf;
Packit Service ac8aad
	size = sec->sh.sh_size;
Packit Service ac8aad
	for (offset = 0; offset < size; offset += length) {
Packit Service ac8aad
		insn_init(&insn1, (void *)(start1 + offset), 1);
Packit Service ac8aad
		insn_init(&insn2, (void *)(start2 + offset), 1);
Packit Service ac8aad
		insn_get_length(&insn1);
Packit Service ac8aad
		insn_get_length(&insn2);
Packit Service ac8aad
		length = insn1.length;
Packit Service ac8aad
Packit Service ac8aad
		if (!insn1.length || !insn2.length)
Packit Service ac8aad
			ERROR("can't decode instruction in section %s at offset 0x%lx",
Packit Service ac8aad
			      sec->name, offset);
Packit Service ac8aad
Packit Service ac8aad
		if (insn1.length != insn2.length)
Packit Service ac8aad
			return 0;
Packit Service ac8aad
Packit Service ac8aad
		if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
Packit Service ac8aad
			    length))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		/* verify it's a mov immediate to %edx or %esi */
Packit Service ac8aad
		insn_get_opcode(&insn1);
Packit Service ac8aad
		insn_get_opcode(&insn2);
Packit Service ac8aad
		if (!(insn1.opcode.value == 0xba && insn2.opcode.value == 0xba) &&
Packit Service ac8aad
		    !(insn1.opcode.value == 0xbe && insn2.opcode.value == 0xbe))
Packit Service ac8aad
			return 0;
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Verify zero or more string relas followed by a
Packit Service ac8aad
		 * warn_slowpath_* or another similar rela.
Packit Service ac8aad
		 */
Packit Service ac8aad
		found = 0;
Packit Service ac8aad
		list_for_each_entry(rela, &sec->rela->relas, list) {
Packit Service ac8aad
			if (rela->offset < offset + length)
Packit Service ac8aad
				continue;
Packit Service ac8aad
			if (rela->string)
Packit Service ac8aad
				continue;
Packit Service ac8aad
			if (!strncmp(rela->sym->name, "__warned.", 9))
Packit Service ac8aad
				continue;
Packit Service ac8aad
			if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "__warn_printk")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "__might_sleep")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "___might_sleep")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "__might_fault")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "printk")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
Packit Service ac8aad
				found = 1;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
			return 0;
Packit Service ac8aad
		}
Packit Service ac8aad
		if (!found)
Packit Service ac8aad
			return 0;
Packit Service ac8aad
Packit Service ac8aad
		lineonly = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!lineonly)
Packit Service ac8aad
		ERROR("no instruction changes detected for changed section %s",
Packit Service ac8aad
		      sec->name);
Packit Service ac8aad
Packit Service ac8aad
	return 1;
Packit Service ac8aad
}
Packit Service ac8aad
#elif __powerpc64__
Packit Service ac8aad
#define PPC_INSTR_LEN 4
Packit Service ac8aad
#define PPC_RA_OFFSET 16
Packit Service ac8aad
Packit Service ac8aad
static int kpatch_line_macro_change_only(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	unsigned long start1, start2, size, offset;
Packit Service ac8aad
	unsigned int instr1, instr2;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int lineonly = 0, found;
Packit Service ac8aad
Packit Service ac8aad
	if (sec->status != CHANGED ||
Packit Service ac8aad
	    is_rela_section(sec) ||
Packit Service ac8aad
	    !is_text_section(sec) ||
Packit Service ac8aad
	    sec->sh.sh_size != sec->twin->sh.sh_size ||
Packit Service ac8aad
	    !sec->rela ||
Packit Service ac8aad
	    sec->rela->status != SAME)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	start1 = (unsigned long)sec->twin->data->d_buf;
Packit Service ac8aad
	start2 = (unsigned long)sec->data->d_buf;
Packit Service ac8aad
	size = sec->sh.sh_size;
Packit Service ac8aad
	for (offset = 0; offset < size; offset += PPC_INSTR_LEN) {
Packit Service ac8aad
		if (!memcmp((void *)start1 + offset, (void *)start2 + offset,
Packit Service ac8aad
			    PPC_INSTR_LEN))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		instr1 = *(unsigned int *)(start1 + offset) >> PPC_RA_OFFSET;
Packit Service ac8aad
		instr2 = *(unsigned int *)(start2 + offset) >> PPC_RA_OFFSET;
Packit Service ac8aad
Packit Service ac8aad
		/* verify it's a load immediate to r5 */
Packit Service ac8aad
		if (!(instr1 == 0x38a0 && instr2 == 0x38a0))
Packit Service ac8aad
			return 0;
Packit Service ac8aad
Packit Service ac8aad
		found = 0;
Packit Service ac8aad
		list_for_each_entry(rela, &sec->rela->relas, list) {
Packit Service ac8aad
			if (rela->offset < offset + PPC_INSTR_LEN)
Packit Service ac8aad
				continue;
Packit Service ac8aad
			if (toc_rela(rela) && toc_rela(rela)->string)
Packit Service ac8aad
				continue;
Packit Service ac8aad
			if (!strncmp(rela->sym->name, "__warned.", 9))
Packit Service ac8aad
				continue;
Packit Service ac8aad
			if (!strncmp(rela->sym->name, "warn_slowpath_", 14) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "__warn_printk")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "__might_sleep")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "___might_sleep")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "__might_fault")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "printk")) ||
Packit Service ac8aad
			   (!strcmp(rela->sym->name, "lockdep_rcu_suspicious"))) {
Packit Service ac8aad
				found = 1;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
			return 0;
Packit Service ac8aad
		}
Packit Service ac8aad
		if (!found)
Packit Service ac8aad
			return 0;
Packit Service ac8aad
Packit Service ac8aad
		lineonly = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!lineonly)
Packit Service ac8aad
		ERROR("no instruction changes detected for changed section %s",
Packit Service ac8aad
		      sec->name);
Packit Service ac8aad
Packit Service ac8aad
	return 1;
Packit Service ac8aad
}
Packit Service ac8aad
#else
Packit Service ac8aad
static int kpatch_line_macro_change_only(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	return 0;
Packit Service ac8aad
}
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_sections(struct list_head *seclist)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
Packit Service ac8aad
	/* compare all sections */
Packit Service ac8aad
	list_for_each_entry(sec, seclist, list) {
Packit Service ac8aad
		if (sec->twin)
Packit Service ac8aad
			kpatch_compare_correlated_section(sec);
Packit Service ac8aad
		else
Packit Service ac8aad
			sec->status = NEW;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* exclude WARN-only, might_sleep changes */
Packit Service ac8aad
	list_for_each_entry(sec, seclist, list) {
Packit Service ac8aad
		if (kpatch_line_macro_change_only(sec)) {
Packit Service ac8aad
			log_debug("reverting macro / line number section %s status to SAME\n",
Packit Service ac8aad
				  sec->name);
Packit Service ac8aad
			sec->status = SAME;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* sync symbol status */
Packit Service ac8aad
	list_for_each_entry(sec, seclist, list) {
Packit Service ac8aad
		if (is_rela_section(sec)) {
Packit Service ac8aad
			if (sec->base->sym && sec->base->sym->status != CHANGED)
Packit Service ac8aad
				sec->base->sym->status = sec->status;
Packit Service ac8aad
		} else {
Packit Service ac8aad
			if (sec->sym && sec->sym->status != CHANGED)
Packit Service ac8aad
				sec->sym->status = sec->status;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_correlated_symbol(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym1 = sym, *sym2 = sym->twin;
Packit Service ac8aad
Packit Service ac8aad
	if (sym1->sym.st_info != sym2->sym.st_info ||
Packit Service ac8aad
	    (sym1->sec && !sym2->sec) ||
Packit Service ac8aad
	    (sym2->sec && !sym1->sec))
Packit Service ac8aad
		DIFF_FATAL("symbol info mismatch: %s", sym1->name);
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * If two symbols are correlated but their sections are not, then the
Packit Service ac8aad
	 * symbol has changed sections.  This is only allowed if the symbol is
Packit Service ac8aad
	 * moving out of an ignored section.
Packit Service ac8aad
	 */
Packit Service ac8aad
	if (sym1->sec && sym2->sec && sym1->sec->twin != sym2->sec) {
Packit Service ac8aad
		if (sym2->sec->twin && sym2->sec->twin->ignore)
Packit Service ac8aad
			sym->status = CHANGED;
Packit Service ac8aad
		else
Packit Service ac8aad
			DIFF_FATAL("symbol changed sections: %s", sym1->name);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (sym1->type == STT_OBJECT &&
Packit Service ac8aad
	    sym1->sym.st_size != sym2->sym.st_size)
Packit Service ac8aad
		DIFF_FATAL("object size mismatch: %s", sym1->name);
Packit Service ac8aad
Packit Service ac8aad
	if (sym1->sym.st_shndx == SHN_UNDEF ||
Packit Service ac8aad
	     sym1->sym.st_shndx == SHN_ABS)
Packit Service ac8aad
		sym1->status = SAME;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * The status of LOCAL symbols is dependent on the status of their
Packit Service ac8aad
	 * matching section and is set during section comparison.
Packit Service ac8aad
	 */
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_symbols(struct list_head *symlist)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, symlist, list) {
Packit Service ac8aad
		if (sym->twin)
Packit Service ac8aad
			kpatch_compare_correlated_symbol(sym);
Packit Service ac8aad
		else
Packit Service ac8aad
			sym->status = NEW;
Packit Service ac8aad
Packit Service ac8aad
		log_debug("symbol %s is %s\n", sym->name, status_str(sym->status));
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_correlate_sections(struct list_head *seclist1, struct list_head *seclist2)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec1, *sec2;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec1, seclist1, list) {
Packit Service ac8aad
		list_for_each_entry(sec2, seclist2, list) {
Packit Service ac8aad
			if (strcmp(sec1->name, sec2->name))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			if (is_special_static(is_rela_section(sec1) ?
Packit Service ac8aad
					      sec1->base->secsym :
Packit Service ac8aad
					      sec1->secsym))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Group sections must match exactly to be correlated.
Packit Service ac8aad
			 * Changed group sections are currently not supported.
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (sec1->sh.sh_type == SHT_GROUP) {
Packit Service ac8aad
				if (sec1->data->d_size != sec2->data->d_size)
Packit Service ac8aad
					continue;
Packit Service ac8aad
				if (memcmp(sec1->data->d_buf, sec2->data->d_buf,
Packit Service ac8aad
				           sec1->data->d_size))
Packit Service ac8aad
					continue;
Packit Service ac8aad
			}
Packit Service ac8aad
			sec1->twin = sec2;
Packit Service ac8aad
			sec2->twin = sec1;
Packit Service ac8aad
			/* set initial status, might change */
Packit Service ac8aad
			sec1->status = sec2->status = SAME;
Packit Service ac8aad
			break;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_correlate_symbols(struct list_head *symlist1, struct list_head *symlist2)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym1, *sym2;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym1, symlist1, list) {
Packit Service ac8aad
		list_for_each_entry(sym2, symlist2, list) {
Packit Service ac8aad
			if (strcmp(sym1->name, sym2->name) ||
Packit Service ac8aad
			    sym1->type != sym2->type)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			if (is_special_static(sym1))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * The .LCx symbols point to strings, usually used for
Packit Service ac8aad
			 * the bug table.  Don't correlate and compare the
Packit Service ac8aad
			 * symbols themselves, because the suffix number might
Packit Service ac8aad
			 * change.
Packit Service ac8aad
			 *
Packit Service ac8aad
			 * If the symbol is used by the bug table (usual case),
Packit Service ac8aad
			 * it may get pulled in by
Packit Service ac8aad
			 * kpatch_regenerate_special_section().
Packit Service ac8aad
			 *
Packit Service ac8aad
			 * If the symbol is used outside of the bug table (not
Packit Service ac8aad
			 * sure if this actually happens anywhere), any string
Packit Service ac8aad
			 * changes will be detected elsewhere in rela_equal().
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (sym1->type == STT_NOTYPE &&
Packit Service ac8aad
			    !strncmp(sym1->name, ".LC", 3))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			/* group section symbols must have correlated sections */
Packit Service ac8aad
			if (sym1->sec &&
Packit Service ac8aad
			    sym1->sec->sh.sh_type == SHT_GROUP &&
Packit Service ac8aad
			    sym1->sec->twin != sym2->sec)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			sym1->twin = sym2;
Packit Service ac8aad
			sym2->twin = sym1;
Packit Service ac8aad
			/* set initial status, might change */
Packit Service ac8aad
			sym1->status = sym2->status = SAME;
Packit Service ac8aad
			break;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_elf_headers(Elf *elf1, Elf *elf2)
Packit Service ac8aad
{
Packit Service ac8aad
	GElf_Ehdr eh1, eh2;
Packit Service ac8aad
Packit Service ac8aad
	if (!gelf_getehdr(elf1, &eh1))
Packit Service ac8aad
		ERROR("gelf_getehdr");
Packit Service ac8aad
Packit Service ac8aad
	if (!gelf_getehdr(elf2, &eh2))
Packit Service ac8aad
		ERROR("gelf_getehdr");
Packit Service ac8aad
Packit Service ac8aad
	if (memcmp(eh1.e_ident, eh2.e_ident, EI_NIDENT) ||
Packit Service ac8aad
	    eh1.e_type != eh2.e_type ||
Packit Service ac8aad
	    eh1.e_machine != eh2.e_machine ||
Packit Service ac8aad
	    eh1.e_version != eh2.e_version ||
Packit Service ac8aad
	    eh1.e_entry != eh2.e_entry ||
Packit Service ac8aad
	    eh1.e_phoff != eh2.e_phoff ||
Packit Service ac8aad
	    eh1.e_flags != eh2.e_flags ||
Packit Service ac8aad
	    eh1.e_ehsize != eh2.e_ehsize ||
Packit Service ac8aad
	    eh1.e_phentsize != eh2.e_phentsize ||
Packit Service ac8aad
	    eh1.e_shentsize != eh2.e_shentsize)
Packit Service ac8aad
		DIFF_FATAL("ELF headers differ");
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_check_program_headers(Elf *elf)
Packit Service ac8aad
{
Packit Service ac8aad
	size_t ph_nr;
Packit Service ac8aad
Packit Service ac8aad
	if (elf_getphdrnum(elf, &ph_nr))
Packit Service ac8aad
		ERROR("elf_getphdrnum");
Packit Service ac8aad
Packit Service ac8aad
	if (ph_nr != 0)
Packit Service ac8aad
		DIFF_FATAL("ELF contains program header");
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_mark_grouped_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *groupsec, *sec;
Packit Service ac8aad
	unsigned int *data, *end;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(groupsec, &kelf->sections, list) {
Packit Service ac8aad
		if (groupsec->sh.sh_type != SHT_GROUP)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		data = groupsec->data->d_buf;
Packit Service ac8aad
		end = groupsec->data->d_buf + groupsec->data->d_size;
Packit Service ac8aad
		data++; /* skip first flag word (e.g. GRP_COMDAT) */
Packit Service ac8aad
		while (data < end) {
Packit Service ac8aad
			sec = find_section_by_index(&kelf->sections, *data);
Packit Service ac8aad
			if (!sec)
Packit Service ac8aad
				ERROR("group section not found");
Packit Service ac8aad
			sec->grouped = 1;
Packit Service ac8aad
			log_debug("marking section %s (%d) as grouped\n",
Packit Service ac8aad
			          sec->name, sec->index);
Packit Service ac8aad
			data++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * When gcc makes compiler optimizations which affect a function's calling
Packit Service ac8aad
 * interface, it mangles the function's name.  For example, sysctl_print_dir is
Packit Service ac8aad
 * renamed to sysctl_print_dir.isra.2.  The problem is that the trailing number
Packit Service ac8aad
 * is chosen arbitrarily, and the patched version of the function may end up
Packit Service ac8aad
 * with a different trailing number.  Rename any mangled patched functions to
Packit Service ac8aad
 * match their base counterparts.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_rename_mangled_functions(struct kpatch_elf *base,
Packit Service ac8aad
					    struct kpatch_elf *patched)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym, *basesym;
Packit Service ac8aad
	char name[256], *origname;
Packit Service ac8aad
	struct section *sec, *basesec;
Packit Service ac8aad
	int found;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &patched->symbols, list) {
Packit Service ac8aad
		if (sym->type != STT_FUNC)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		if (!strstr(sym->name, ".isra.") &&
Packit Service ac8aad
		    !strstr(sym->name, ".constprop.") &&
Packit Service ac8aad
		    !strstr(sym->name, ".cold.") &&
Packit Service ac8aad
		    !strstr(sym->name, ".part."))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		found = 0;
Packit Service ac8aad
		list_for_each_entry(basesym, &base->symbols, list) {
Packit Service ac8aad
			if (!kpatch_mangled_strcmp(basesym->name, sym->name)) {
Packit Service ac8aad
				found = 1;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		if (!found)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		if (!strcmp(sym->name, basesym->name))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		log_debug("renaming %s to %s\n", sym->name, basesym->name);
Packit Service ac8aad
		origname = sym->name;
Packit Service ac8aad
		sym->name = strdup(basesym->name);
Packit Service ac8aad
Packit Service ac8aad
		if (sym != sym->sec->sym)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sym->sec->name = strdup(basesym->sec->name);
Packit Service ac8aad
		if (sym->sec->rela)
Packit Service ac8aad
			sym->sec->rela->name = strdup(basesym->sec->rela->name);
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * When function foo.isra.1 has a switch statement, it might
Packit Service ac8aad
		 * have a corresponding bundled .rodata.foo.isra.1 section (in
Packit Service ac8aad
		 * addition to .text.foo.isra.1 which we renamed above).
Packit Service ac8aad
		 */
Packit Service ac8aad
		sprintf(name, ".rodata.%s", origname);
Packit Service ac8aad
		sec = find_section_by_name(&patched->sections, name);
Packit Service ac8aad
		if (!sec)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		sprintf(name, ".rodata.%s", basesym->name);
Packit Service ac8aad
		basesec = find_section_by_name(&base->sections, name);
Packit Service ac8aad
		if (!basesec)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		sec->name = strdup(basesec->name);
Packit Service ac8aad
		sec->secsym->name = sec->name;
Packit Service ac8aad
		if (sec->rela)
Packit Service ac8aad
			sec->rela->name = strdup(basesec->rela->name);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static char *kpatch_section_function_name(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	if (is_rela_section(sec))
Packit Service ac8aad
		sec = sec->base;
Packit Service ac8aad
	return sec->sym ? sec->sym->name : sec->name;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * Given a static local variable symbol and a rela section which references it
Packit Service ac8aad
 * in the base object, find a corresponding usage of a similarly named symbol
Packit Service ac8aad
 * in the patched object.
Packit Service ac8aad
 */
Packit Service ac8aad
static struct symbol *kpatch_find_static_twin(struct section *sec,
Packit Service ac8aad
					      struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela, *rela_toc;
Packit Service ac8aad
Packit Service ac8aad
	if (!sec->twin)
Packit Service ac8aad
		return NULL;
Packit Service ac8aad
Packit Service ac8aad
	/* find the patched object's corresponding variable */
Packit Service ac8aad
	list_for_each_entry(rela, &sec->twin->relas, list) {
Packit Service ac8aad
Packit Service ac8aad
		rela_toc = toc_rela(rela);
Packit Service ac8aad
		if (!rela_toc)
Packit Service ac8aad
			continue; /* skip toc constants */
Packit Service ac8aad
Packit Service ac8aad
		if (rela_toc->sym->twin)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		if (kpatch_mangled_strcmp(rela_toc->sym->name, sym->name))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		return rela_toc->sym;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int kpatch_is_normal_static_local(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	if (!strchr(sym->name, '.'))
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	if (is_special_static(sym))
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	return 1;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * gcc renames static local variables by appending a period and a number.  For
Packit Service ac8aad
 * example, __foo could be renamed to __foo.31452.  Unfortunately this number
Packit Service ac8aad
 * can arbitrarily change.  Correlate them by comparing which functions
Packit Service ac8aad
 * reference them, and rename the patched symbols to match the base symbol
Packit Service ac8aad
 * names.
Packit Service ac8aad
 *
Packit Service ac8aad
 * Some surprising facts about static local variable symbols:
Packit Service ac8aad
 *
Packit Service ac8aad
 * - It's possible for multiple functions to use the same
Packit Service ac8aad
 *   static local variable if the variable is defined in an
Packit Service ac8aad
 *   inlined function.
Packit Service ac8aad
 *
Packit Service ac8aad
 * - It's also possible for multiple static local variables
Packit Service ac8aad
 *   with the same name to be used in the same function if they
Packit Service ac8aad
 *   have different scopes.  (We have to assume that in such
Packit Service ac8aad
 *   cases, the order in which they're referenced remains the
Packit Service ac8aad
 *   same between the base and patched objects, as there's no
Packit Service ac8aad
 *   other way to distinguish them.)
Packit Service ac8aad
 *
Packit Service ac8aad
 * - Static locals are usually referenced by functions, but
Packit Service ac8aad
 *   they can occasionally be referenced by data sections as
Packit Service ac8aad
 *   well.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_correlate_static_local_variables(struct kpatch_elf *base,
Packit Service ac8aad
						    struct kpatch_elf *patched)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym, *patched_sym;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct rela *rela, *rela2;
Packit Service ac8aad
	int bundled, patched_bundled, found;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * First undo the correlations for all static locals.  Two static
Packit Service ac8aad
	 * locals can have the same numbered suffix in the base and patched
Packit Service ac8aad
	 * objects by coincidence.
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sym, &base->symbols, list) {
Packit Service ac8aad
Packit Service ac8aad
		if (!kpatch_is_normal_static_local(sym))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		if (sym->twin) {
Packit Service ac8aad
			sym->twin->twin = NULL;
Packit Service ac8aad
			sym->twin = NULL;
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		bundled = sym == sym->sec->sym;
Packit Service ac8aad
		if (bundled && sym->sec->twin) {
Packit Service ac8aad
			sym->sec->twin->twin = NULL;
Packit Service ac8aad
			sym->sec->twin = NULL;
Packit Service ac8aad
Packit Service ac8aad
			sym->sec->secsym->twin->twin = NULL;
Packit Service ac8aad
			sym->sec->secsym->twin = NULL;
Packit Service ac8aad
Packit Service ac8aad
			if (sym->sec->rela) {
Packit Service ac8aad
				sym->sec->rela->twin->twin = NULL;
Packit Service ac8aad
				sym->sec->rela->twin = NULL;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Do the correlations: for each section reference to a static local,
Packit Service ac8aad
	 * look for a corresponding reference in the section's twin.
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sec, &base->sections, list) {
Packit Service ac8aad
Packit Service ac8aad
		if (!is_rela_section(sec) ||
Packit Service ac8aad
		    is_debug_section(sec) ||
Packit Service ac8aad
		    !strcmp(sec->name, ".rela.toc"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
Packit Service ac8aad
			if (!toc_rela(rela))
Packit Service ac8aad
				continue; /* skip toc constants */
Packit Service ac8aad
			sym = toc_rela(rela)->sym;
Packit Service ac8aad
Packit Service ac8aad
			if (!kpatch_is_normal_static_local(sym))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			if (sym->twin)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			bundled = sym == sym->sec->sym;
Packit Service ac8aad
			if (bundled && sym->sec == sec->base) {
Packit Service ac8aad
				/*
Packit Service ac8aad
				 * A rare case where a static local data
Packit Service ac8aad
				 * structure references itself.  There's no
Packit Service ac8aad
				 * reliable way to correlate this.  Hopefully
Packit Service ac8aad
				 * there's another reference to the symbol
Packit Service ac8aad
				 * somewhere that can be used.
Packit Service ac8aad
				 */
Packit Service ac8aad
				log_debug("can't correlate static local %s's reference to itself\n",
Packit Service ac8aad
					  sym->name);
Packit Service ac8aad
				continue;
Packit Service ac8aad
			}
Packit Service ac8aad
Packit Service ac8aad
			patched_sym = kpatch_find_static_twin(sec, sym);
Packit Service ac8aad
			if (!patched_sym)
Packit Service ac8aad
				DIFF_FATAL("reference to static local variable %s in %s was removed",
Packit Service ac8aad
					   sym->name,
Packit Service ac8aad
					   kpatch_section_function_name(sec));
Packit Service ac8aad
Packit Service ac8aad
			patched_bundled = patched_sym == patched_sym->sec->sym;
Packit Service ac8aad
			if (bundled != patched_bundled)
Packit Service ac8aad
				ERROR("bundle mismatch for symbol %s", sym->name);
Packit Service ac8aad
			if (!bundled && sym->sec->twin != patched_sym->sec)
Packit Service ac8aad
				ERROR("sections %s and %s aren't correlated",
Packit Service ac8aad
				      sym->sec->name, patched_sym->sec->name);
Packit Service ac8aad
Packit Service ac8aad
			log_debug("renaming and correlating static local %s to %s\n",
Packit Service ac8aad
				  patched_sym->name, sym->name);
Packit Service ac8aad
Packit Service ac8aad
			patched_sym->name = strdup(sym->name);
Packit Service ac8aad
			sym->twin = patched_sym;
Packit Service ac8aad
			patched_sym->twin = sym;
Packit Service ac8aad
Packit Service ac8aad
			/* set initial status, might change */
Packit Service ac8aad
			sym->status = patched_sym->status = SAME;
Packit Service ac8aad
Packit Service ac8aad
			if (bundled) {
Packit Service ac8aad
				sym->sec->twin = patched_sym->sec;
Packit Service ac8aad
				patched_sym->sec->twin = sym->sec;
Packit Service ac8aad
Packit Service ac8aad
				sym->sec->secsym->twin = patched_sym->sec->secsym;
Packit Service ac8aad
				patched_sym->sec->secsym->twin = sym->sec->secsym;
Packit Service ac8aad
Packit Service ac8aad
				if (sym->sec->rela && patched_sym->sec->rela) {
Packit Service ac8aad
					sym->sec->rela->twin = patched_sym->sec->rela;
Packit Service ac8aad
					patched_sym->sec->rela->twin = sym->sec->rela;
Packit Service ac8aad
				}
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Make sure that:
Packit Service ac8aad
	 *
Packit Service ac8aad
	 * 1. all the base object's referenced static locals have been
Packit Service ac8aad
	 *    correlated; and
Packit Service ac8aad
	 *
Packit Service ac8aad
	 * 2. each reference to a static local in the base object has a
Packit Service ac8aad
	 *    corresponding reference in the patched object (because a static
Packit Service ac8aad
	 *    local can be referenced by more than one section).
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sec, &base->sections, list) {
Packit Service ac8aad
Packit Service ac8aad
		if (!is_rela_section(sec) ||
Packit Service ac8aad
		    is_debug_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
Packit Service ac8aad
			sym = rela->sym;
Packit Service ac8aad
			if (!kpatch_is_normal_static_local(sym))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			if (!sym->twin || !sec->twin)
Packit Service ac8aad
				DIFF_FATAL("reference to static local variable %s in %s was removed",
Packit Service ac8aad
					   sym->name,
Packit Service ac8aad
					   kpatch_section_function_name(sec));
Packit Service ac8aad
Packit Service ac8aad
			found = 0;
Packit Service ac8aad
			list_for_each_entry(rela2, &sec->twin->relas, list) {
Packit Service ac8aad
				if (rela2->sym == sym->twin) {
Packit Service ac8aad
					found = 1;
Packit Service ac8aad
					break;
Packit Service ac8aad
				}
Packit Service ac8aad
			}
Packit Service ac8aad
Packit Service ac8aad
			if (!found)
Packit Service ac8aad
				DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it",
Packit Service ac8aad
					   sym->name, sym->twin->name,
Packit Service ac8aad
					   kpatch_section_function_name(sec->twin));
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Now go through the patched object and look for any uncorrelated
Packit Service ac8aad
	 * static locals to see if we need to print any warnings about new
Packit Service ac8aad
	 * variables.
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sec, &patched->sections, list) {
Packit Service ac8aad
Packit Service ac8aad
		if (!is_rela_section(sec) ||
Packit Service ac8aad
		    is_debug_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
Packit Service ac8aad
			sym = rela->sym;
Packit Service ac8aad
			if (!kpatch_is_normal_static_local(sym))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			if (sym->twin)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n",
Packit Service ac8aad
				   sym->name,
Packit Service ac8aad
				   kpatch_section_function_name(sec));
Packit Service ac8aad
			return;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_correlate_elfs(struct kpatch_elf *kelf1, struct kpatch_elf *kelf2)
Packit Service ac8aad
{
Packit Service ac8aad
	kpatch_correlate_sections(&kelf1->sections, &kelf2->sections);
Packit Service ac8aad
	kpatch_correlate_symbols(&kelf1->symbols, &kelf2->symbols);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	/* lists are already correlated at this point */
Packit Service ac8aad
	kpatch_compare_sections(&kelf->sections);
Packit Service ac8aad
	kpatch_compare_symbols(&kelf->symbols);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
#ifdef __x86_64__
Packit Service ac8aad
static void rela_insn(struct section *sec, struct rela *rela, struct insn *insn)
Packit Service ac8aad
{
Packit Service ac8aad
	unsigned long insn_addr, start, end, rela_addr;
Packit Service ac8aad
Packit Service ac8aad
	start = (unsigned long)sec->base->data->d_buf;
Packit Service ac8aad
	end = start + sec->base->sh.sh_size;
Packit Service ac8aad
	rela_addr = start + rela->offset;
Packit Service ac8aad
	for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
Packit Service ac8aad
		insn_init(insn, (void *)insn_addr, 1);
Packit Service ac8aad
		insn_get_length(insn);
Packit Service ac8aad
		if (!insn->length)
Packit Service ac8aad
			ERROR("can't decode instruction in section %s at offset 0x%lx",
Packit Service ac8aad
			      sec->base->name, insn_addr);
Packit Service ac8aad
		if (rela_addr >= insn_addr &&
Packit Service ac8aad
		    rela_addr < insn_addr + insn->length)
Packit Service ac8aad
			return;
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * Mangle the relas a little.  The compiler will sometimes use section symbols
Packit Service ac8aad
 * to reference local objects and functions rather than the object or function
Packit Service ac8aad
 * symbols themselves.  We substitute the object/function symbols for the
Packit Service ac8aad
 * section symbol in this case so that the relas can be properly correlated and
Packit Service ac8aad
 * so that the existing object/function in vmlinux can be linked to.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	int add_off;
Packit Service ac8aad
Packit Service ac8aad
	log_debug("\n");
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!is_rela_section(sec) ||
Packit Service ac8aad
		    is_debug_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
Packit Service ac8aad
			if (rela->sym->type != STT_SECTION)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Replace references to bundled sections with their
Packit Service ac8aad
			 * symbols.
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (rela->sym->sec && rela->sym->sec->sym) {
Packit Service ac8aad
				rela->sym = rela->sym->sec->sym;
Packit Service ac8aad
Packit Service ac8aad
				continue;
Packit Service ac8aad
			}
Packit Service ac8aad
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
			add_off = 0;
Packit Service ac8aad
#else
Packit Service ac8aad
			if (rela->type == R_X86_64_PC32) {
Packit Service ac8aad
				struct insn insn;
Packit Service ac8aad
				rela_insn(sec, rela, &insn);
Packit Service ac8aad
				add_off = (long)insn.next_byte -
Packit Service ac8aad
					  (long)sec->base->data->d_buf -
Packit Service ac8aad
					  rela->offset;
Packit Service ac8aad
			} else if (rela->type == R_X86_64_64 ||
Packit Service ac8aad
				   rela->type == R_X86_64_32S)
Packit Service ac8aad
				add_off = 0;
Packit Service ac8aad
			else
Packit Service ac8aad
				continue;
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Attempt to replace references to unbundled sections
Packit Service ac8aad
			 * with their symbols.
Packit Service ac8aad
			 */
Packit Service ac8aad
			list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
				int start, end;
Packit Service ac8aad
Packit Service ac8aad
				if (sym->type == STT_SECTION ||
Packit Service ac8aad
				    sym->sec != rela->sym->sec)
Packit Service ac8aad
					continue;
Packit Service ac8aad
Packit Service ac8aad
				start = sym->sym.st_value;
Packit Service ac8aad
				end = sym->sym.st_value + sym->sym.st_size;
Packit Service ac8aad
Packit Service ac8aad
				if (!is_text_section(sym->sec) &&
Packit Service ac8aad
				    rela->type == R_X86_64_32S &&
Packit Service ac8aad
				    rela->addend == (int)sym->sec->sh.sh_size &&
Packit Service ac8aad
				    end == (int)sym->sec->sh.sh_size) {
Packit Service ac8aad
Packit Service ac8aad
					/*
Packit Service ac8aad
					 * A special case where gcc needs a
Packit Service ac8aad
					 * pointer to the address at the end of
Packit Service ac8aad
					 * a data section.
Packit Service ac8aad
					 *
Packit Service ac8aad
					 * This is usually used with a compare
Packit Service ac8aad
					 * instruction to determine when to end
Packit Service ac8aad
					 * a loop.  The code doesn't actually
Packit Service ac8aad
					 * dereference the pointer so this is
Packit Service ac8aad
					 * "normal" and we just replace the
Packit Service ac8aad
					 * section reference with a reference
Packit Service ac8aad
					 * to the last symbol in the section.
Packit Service ac8aad
					 *
Packit Service ac8aad
					 * Note that this only catches the
Packit Service ac8aad
					 * issue when it happens at the end of
Packit Service ac8aad
					 * a section.  It can also happen in
Packit Service ac8aad
					 * the middle of a section.  In that
Packit Service ac8aad
					 * case, the wrong symbol will be
Packit Service ac8aad
					 * associated with the reference.  But
Packit Service ac8aad
					 * that's ok because:
Packit Service ac8aad
					 *
Packit Service ac8aad
					 * 1) This situation only occurs when
Packit Service ac8aad
					 *    gcc is trying to get the address
Packit Service ac8aad
					 *    of the symbol, not the contents
Packit Service ac8aad
					 *    of its data; and
Packit Service ac8aad
					 *
Packit Service ac8aad
					 * 2) Because kpatch doesn't allow data
Packit Service ac8aad
					 *    sections to change,
Packit Service ac8aad
					 *    &(var1+sizeof(var1)) will always
Packit Service ac8aad
					 *    be the same as &var2.
Packit Service ac8aad
					 */
Packit Service ac8aad
Packit Service ac8aad
				} else if (rela->addend + add_off < start ||
Packit Service ac8aad
					   rela->addend + add_off >= end)
Packit Service ac8aad
					continue;
Packit Service ac8aad
Packit Service ac8aad
				log_debug("%s: replacing %s+%d reference with %s+%d\n",
Packit Service ac8aad
					  sec->name,
Packit Service ac8aad
					  rela->sym->name, rela->addend,
Packit Service ac8aad
					  sym->name, rela->addend - start);
Packit Service ac8aad
Packit Service ac8aad
				rela->sym = sym;
Packit Service ac8aad
				rela->addend -= start;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
	log_debug("\n");
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_check_func_profiling_calls(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	int errs = 0;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->type != STT_FUNC || sym->status != CHANGED)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		if (!sym->twin->has_func_profiling) {
Packit Service ac8aad
			log_normal("function %s has no fentry/mcount call, unable to patch\n",
Packit Service ac8aad
				   sym->name);
Packit Service ac8aad
			errs++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (errs)
Packit Service ac8aad
		DIFF_FATAL("%d function(s) can not be patched", errs);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_verify_patchability(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	int errs = 0;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (sec->status == CHANGED && !sec->include) {
Packit Service ac8aad
			log_normal("changed section %s not selected for inclusion\n",
Packit Service ac8aad
				   sec->name);
Packit Service ac8aad
			errs++;
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		if (sec->status != SAME && sec->grouped) {
Packit Service ac8aad
			log_normal("changed section %s is part of a section group\n",
Packit Service ac8aad
				   sec->name);
Packit Service ac8aad
			errs++;
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) {
Packit Service ac8aad
			log_normal("new/changed group sections are not supported\n");
Packit Service ac8aad
			errs++;
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * ensure we aren't including .data.* or .bss.*
Packit Service ac8aad
		 * (.data.unlikely and .data.once is ok b/c it only has __warned vars)
Packit Service ac8aad
		 */
Packit Service ac8aad
		if (sec->include && sec->status != NEW &&
Packit Service ac8aad
		    (!strncmp(sec->name, ".data", 5) || !strncmp(sec->name, ".bss", 4)) &&
Packit Service ac8aad
		    (strcmp(sec->name, ".data.unlikely") && strcmp(sec->name, ".data.once"))) {
Packit Service ac8aad
			log_normal("data section %s selected for inclusion\n",
Packit Service ac8aad
				   sec->name);
Packit Service ac8aad
			errs++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (errs)
Packit Service ac8aad
		DIFF_FATAL("%d unsupported section change(s)", errs);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_include_symbol(struct symbol *sym);
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_include_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
Packit Service ac8aad
	/* Include the section and its section symbol */
Packit Service ac8aad
	if (sec->include)
Packit Service ac8aad
		return;
Packit Service ac8aad
	sec->include = 1;
Packit Service ac8aad
	if (sec->secsym)
Packit Service ac8aad
		sec->secsym->include = 1;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Include the section's rela section and then recursively include the
Packit Service ac8aad
	 * symbols needed by its relas.
Packit Service ac8aad
	 */
Packit Service ac8aad
	if (!sec->rela)
Packit Service ac8aad
		return;
Packit Service ac8aad
	sec->rela->include = 1;
Packit Service ac8aad
	list_for_each_entry(rela, &sec->rela->relas, list)
Packit Service ac8aad
		kpatch_include_symbol(rela->sym);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_include_symbol(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * This function is called recursively from kpatch_include_section().
Packit Service ac8aad
	 * Make sure we don't get into an endless loop.
Packit Service ac8aad
	 */
Packit Service ac8aad
	if (sym->include)
Packit Service ac8aad
		return;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * The symbol gets included even if its section isn't needed, as it
Packit Service ac8aad
	 * might be needed: either permanently for a rela, or temporarily for
Packit Service ac8aad
	 * the later creation of a dynrela.
Packit Service ac8aad
	 */
Packit Service ac8aad
	sym->include = 1;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * For a function/object symbol, if it has a section, we only need to
Packit Service ac8aad
	 * include the section if it has changed.  Otherwise the symbol will be
Packit Service ac8aad
	 * used by relas/dynrelas to link to the real symbol externally.
Packit Service ac8aad
	 *
Packit Service ac8aad
	 * For section symbols, we always include the section because
Packit Service ac8aad
	 * references to them can't otherwise be resolved externally.
Packit Service ac8aad
	 */
Packit Service ac8aad
	if (sym->sec && (sym->type == STT_SECTION || sym->status != SAME))
Packit Service ac8aad
		kpatch_include_section(sym->sec);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Include the following sections even if they haven't changed.
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * Notes about some of the more interesting sections:
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * - With -fdata-sections, .rodata is only used for:
Packit Service ac8aad
		 *
Packit Service ac8aad
		 *     switch jump tables;
Packit Service ac8aad
		 *     KASAN data (with KASAN enabled, which is rare); and
Packit Service ac8aad
		 *     an ugly hack in vmx_vcpu_run().
Packit Service ac8aad
		 *
Packit Service ac8aad
		 *   Those data are all local to the functions which use them.
Packit Service ac8aad
		 *   So it's safe to include .rodata.
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * - On ppc64le, the .toc section is used for all data
Packit Service ac8aad
		 *   accesses.
Packit Service ac8aad
		 *
Packit Service ac8aad
		 * Note that if any of these sections have rela sections, they
Packit Service ac8aad
		 * will also be included in their entirety.  That may result in
Packit Service ac8aad
		 * some extra (unused) dynrelas getting created, which should
Packit Service ac8aad
		 * be harmless.
Packit Service ac8aad
		 */
Packit Service ac8aad
		if (!strcmp(sec->name, ".shstrtab") ||
Packit Service ac8aad
		    !strcmp(sec->name, ".strtab") ||
Packit Service ac8aad
		    !strcmp(sec->name, ".symtab") ||
Packit Service ac8aad
		    !strcmp(sec->name, ".toc") ||
Packit Service ac8aad
		    !strcmp(sec->name, ".rodata") ||
Packit Service ac8aad
		    (!strncmp(sec->name, ".rodata.", 8) &&
Packit Service ac8aad
		     strstr(sec->name, ".str1."))) {
Packit Service ac8aad
			kpatch_include_section(sec);
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* include the NULL symbol */
Packit Service ac8aad
	list_entry(kelf->symbols.next, struct symbol, list)->include = 1;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int kpatch_include_callback_elements(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int found = 0;
Packit Service ac8aad
Packit Service ac8aad
	static char *callback_sections[] = {
Packit Service ac8aad
		".kpatch.callbacks.pre_patch",
Packit Service ac8aad
		".kpatch.callbacks.post_patch",
Packit Service ac8aad
		".kpatch.callbacks.pre_unpatch",
Packit Service ac8aad
		".kpatch.callbacks.post_unpatch",
Packit Service ac8aad
		".rela.kpatch.callbacks.pre_patch",
Packit Service ac8aad
		".rela.kpatch.callbacks.post_patch",
Packit Service ac8aad
		".rela.kpatch.callbacks.pre_unpatch",
Packit Service ac8aad
		".rela.kpatch.callbacks.post_unpatch",
Packit Service ac8aad
		NULL,
Packit Service ac8aad
	};
Packit Service ac8aad
	char **callback_section;
Packit Service ac8aad
Packit Service ac8aad
	/* include load/unload sections */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
Packit Service ac8aad
		for (callback_section = callback_sections; *callback_section; callback_section++) {
Packit Service ac8aad
Packit Service ac8aad
			if (strcmp(*callback_section, sec->name))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			sec->include = 1;
Packit Service ac8aad
			found = 1;
Packit Service ac8aad
			if (is_rela_section(sec)) {
Packit Service ac8aad
				/* include callback dependencies */
Packit Service ac8aad
				rela = list_entry(sec->relas.next,
Packit Service ac8aad
			                         struct rela, list);
Packit Service ac8aad
				sym = rela->sym;
Packit Service ac8aad
				log_normal("found callback: %s\n",sym->name);
Packit Service ac8aad
				kpatch_include_symbol(sym);
Packit Service ac8aad
				/* strip the callback symbol */
Packit Service ac8aad
				sym->include = 0;
Packit Service ac8aad
				sym->sec->sym = NULL;
Packit Service ac8aad
				/* use section symbol instead */
Packit Service ac8aad
				rela->sym = sym->sec->secsym;
Packit Service ac8aad
			} else {
Packit Service ac8aad
				sec->secsym->include = 1;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* Strip temporary global structures used by the callback macros. */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (!sym->sec)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		for (callback_section = callback_sections; *callback_section; callback_section++) {
Packit Service ac8aad
			if (!strcmp(*callback_section, sym->sec->name)) {
Packit Service ac8aad
				sym->include = 0;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return found;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_include_force_elements(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
Packit Service ac8aad
	/* include force sections */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!strcmp(sec->name, ".kpatch.force") ||
Packit Service ac8aad
		    !strcmp(sec->name, ".rela.kpatch.force")) {
Packit Service ac8aad
			sec->include = 1;
Packit Service ac8aad
			if (!is_rela_section(sec)) {
Packit Service ac8aad
				/* .kpatch.force */
Packit Service ac8aad
				sec->secsym->include = 1;
Packit Service ac8aad
				continue;
Packit Service ac8aad
			}
Packit Service ac8aad
			/* .rela.kpatch.force */
Packit Service ac8aad
			list_for_each_entry(rela, &sec->relas, list)
Packit Service ac8aad
				log_normal("function '%s' marked with KPATCH_FORCE_UNSAFE!\n",
Packit Service ac8aad
				           rela->sym->name);
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* strip temporary global kpatch_force_func_* symbols */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
		if (!strncmp(sym->name, "__kpatch_force_func_",
Packit Service ac8aad
		            strlen("__kpatch_force_func_")))
Packit Service ac8aad
			sym->include = 0;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int kpatch_include_new_globals(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	int nr = 0;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->bind == STB_GLOBAL && sym->sec &&
Packit Service ac8aad
		    sym->status == NEW) {
Packit Service ac8aad
			kpatch_include_symbol(sym);
Packit Service ac8aad
			nr++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return nr;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int kpatch_include_changed_functions(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	int changed_nr = 0;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->status == CHANGED &&
Packit Service ac8aad
		    sym->type == STT_FUNC) {
Packit Service ac8aad
			changed_nr++;
Packit Service ac8aad
			kpatch_include_symbol(sym);
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		if (sym->type == STT_FILE)
Packit Service ac8aad
			sym->include = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return changed_nr;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_print_changes(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (!sym->include || !sym->sec || sym->type != STT_FUNC)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		if (sym->status == NEW)
Packit Service ac8aad
			log_normal("new function: %s\n", sym->name);
Packit Service ac8aad
		else if (sym->status == CHANGED)
Packit Service ac8aad
			log_normal("changed function: %s\n", sym->name);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_migrate_symbols(struct list_head *src,
Packit Service ac8aad
				   struct list_head *dst,
Packit Service ac8aad
				   int (*select)(struct symbol *))
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym, *safe;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry_safe(sym, safe, src, list) {
Packit Service ac8aad
		if (select && !select(sym))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		list_del(&sym->list);
Packit Service ac8aad
		list_add_tail(&sym->list, dst);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_migrate_included_elements(struct kpatch_elf *kelf, struct kpatch_elf **kelfout)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec, *safesec;
Packit Service ac8aad
	struct symbol *sym, *safesym;
Packit Service ac8aad
	struct kpatch_elf *out;
Packit Service ac8aad
Packit Service ac8aad
	/* allocate output kelf */
Packit Service ac8aad
	out = malloc(sizeof(*out));
Packit Service ac8aad
	if (!out)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	memset(out, 0, sizeof(*out));
Packit Service ac8aad
	INIT_LIST_HEAD(&out->sections);
Packit Service ac8aad
	INIT_LIST_HEAD(&out->symbols);
Packit Service ac8aad
	INIT_LIST_HEAD(&out->strings);
Packit Service ac8aad
Packit Service ac8aad
	/* migrate included sections from kelf to out */
Packit Service ac8aad
	list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
Packit Service ac8aad
		if (!sec->include)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		list_del(&sec->list);
Packit Service ac8aad
		list_add_tail(&sec->list, &out->sections);
Packit Service ac8aad
		sec->index = 0;
Packit Service ac8aad
		if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include)
Packit Service ac8aad
			/* break link to non-included section symbol */
Packit Service ac8aad
			sec->secsym = NULL;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* migrate included symbols from kelf to out */
Packit Service ac8aad
	list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
Packit Service ac8aad
		if (!sym->include)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		list_del(&sym->list);
Packit Service ac8aad
		list_add_tail(&sym->list, &out->symbols);
Packit Service ac8aad
		sym->index = 0;
Packit Service ac8aad
		sym->strip = 0;
Packit Service ac8aad
		if (sym->sec && !sym->sec->include)
Packit Service ac8aad
			/* break link to non-included section */
Packit Service ac8aad
			sym->sec = NULL;
Packit Service ac8aad
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	*kelfout = out;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_reorder_symbols(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	LIST_HEAD(symbols);
Packit Service ac8aad
Packit Service ac8aad
	/* migrate NULL sym */
Packit Service ac8aad
	kpatch_migrate_symbols(&kelf->symbols, &symbols, is_null_sym);
Packit Service ac8aad
	/* migrate LOCAL FILE sym */
Packit Service ac8aad
	kpatch_migrate_symbols(&kelf->symbols, &symbols, is_file_sym);
Packit Service ac8aad
	/* migrate LOCAL FUNC syms */
Packit Service ac8aad
	kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_func_sym);
Packit Service ac8aad
	/* migrate all other LOCAL syms */
Packit Service ac8aad
	kpatch_migrate_symbols(&kelf->symbols, &symbols, is_local_sym);
Packit Service ac8aad
	/* migrate all other (GLOBAL) syms */
Packit Service ac8aad
	kpatch_migrate_symbols(&kelf->symbols, &symbols, NULL);
Packit Service ac8aad
Packit Service ac8aad
	list_replace(&symbols, &kelf->symbols);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int bug_table_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	static int size = 0;
Packit Service ac8aad
	char *str;
Packit Service ac8aad
Packit Service ac8aad
	if (!size) {
Packit Service ac8aad
		str = getenv("BUG_STRUCT_SIZE");
Packit Service ac8aad
		if (!str)
Packit Service ac8aad
			ERROR("BUG_STRUCT_SIZE not set");
Packit Service ac8aad
		size = atoi(str);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int ex_table_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	static int size = 0;
Packit Service ac8aad
	char *str;
Packit Service ac8aad
Packit Service ac8aad
	if (!size) {
Packit Service ac8aad
		str = getenv("EX_STRUCT_SIZE");
Packit Service ac8aad
		if (!str)
Packit Service ac8aad
			ERROR("EX_STRUCT_SIZE not set");
Packit Service ac8aad
		size = atoi(str);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
#ifdef __x86_64__
Packit Service ac8aad
static int parainstructions_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	static int size = 0;
Packit Service ac8aad
	char *str;
Packit Service ac8aad
Packit Service ac8aad
	if (!size) {
Packit Service ac8aad
		str = getenv("PARA_STRUCT_SIZE");
Packit Service ac8aad
		if (!str)
Packit Service ac8aad
			ERROR("PARA_STRUCT_SIZE not set");
Packit Service ac8aad
		size = atoi(str);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int altinstructions_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	static int size = 0;
Packit Service ac8aad
	char *str;
Packit Service ac8aad
Packit Service ac8aad
	if (!size) {
Packit Service ac8aad
		str = getenv("ALT_STRUCT_SIZE");
Packit Service ac8aad
		if (!str)
Packit Service ac8aad
			ERROR("ALT_STRUCT_SIZE not set");
Packit Service ac8aad
		size = atoi(str);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int smp_locks_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	return 4;
Packit Service ac8aad
}
Packit Service ac8aad
#endif
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
static int fixup_entry_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	static int size = 0;
Packit Service ac8aad
	char *str;
Packit Service ac8aad
Packit Service ac8aad
	if (!size) {
Packit Service ac8aad
		str = getenv("FIXUP_STRUCT_SIZE");
Packit Service ac8aad
		if (!str)
Packit Service ac8aad
			ERROR("FIXUP_STRUCT_SIZE not set");
Packit Service ac8aad
		size = atoi(str);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int fixup_lwsync_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	return 4;
Packit Service ac8aad
}
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * The rela groups in the .fixup section vary in size.  The beginning of each
Packit Service ac8aad
 * .fixup rela group is referenced by the __ex_table section. To find the size
Packit Service ac8aad
 * of a .fixup rela group, we have to traverse the __ex_table relas.
Packit Service ac8aad
 */
Packit Service ac8aad
static int fixup_group_size(struct kpatch_elf *kelf, int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int found;
Packit Service ac8aad
Packit Service ac8aad
	sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
Packit Service ac8aad
	if (!sec)
Packit Service ac8aad
		ERROR("missing .rela__ex_table section");
Packit Service ac8aad
Packit Service ac8aad
	/* find beginning of this group */
Packit Service ac8aad
	found = 0;
Packit Service ac8aad
	list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
		if (!strcmp(rela->sym->name, ".fixup") &&
Packit Service ac8aad
		    rela->addend == offset) {
Packit Service ac8aad
				found = 1;
Packit Service ac8aad
				break;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!found)
Packit Service ac8aad
		ERROR("can't find .fixup rela group at offset %d\n", offset);
Packit Service ac8aad
Packit Service ac8aad
	/* find beginning of next group */
Packit Service ac8aad
	found = 0;
Packit Service ac8aad
	list_for_each_entry_continue(rela, &sec->relas, list) {
Packit Service ac8aad
		if (!strcmp(rela->sym->name, ".fixup") &&
Packit Service ac8aad
		    rela->addend > offset) {
Packit Service ac8aad
			found = 1;
Packit Service ac8aad
			break;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!found) {
Packit Service ac8aad
		/* last group */
Packit Service ac8aad
		struct section *fixupsec;
Packit Service ac8aad
		fixupsec = find_section_by_name(&kelf->sections, ".fixup");
Packit Service ac8aad
		return fixupsec->sh.sh_size - offset;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return rela->addend - offset;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static struct special_section special_sections[] = {
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= "__bug_table",
Packit Service ac8aad
		.group_size	= bug_table_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
#ifdef __x86_64__
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= ".smp_locks",
Packit Service ac8aad
		.group_size	= smp_locks_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= ".parainstructions",
Packit Service ac8aad
		.group_size	= parainstructions_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
#endif
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= ".fixup",
Packit Service ac8aad
		.group_size	= fixup_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= "__ex_table", /* must come after .fixup */
Packit Service ac8aad
		.group_size	= ex_table_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
#ifdef __x86_64__
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= ".altinstructions",
Packit Service ac8aad
		.group_size	= altinstructions_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
#endif
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= "__ftr_fixup",
Packit Service ac8aad
		.group_size	= fixup_entry_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= "__mmu_ftr_fixup",
Packit Service ac8aad
		.group_size	= fixup_entry_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= "__fw_ftr_fixup",
Packit Service ac8aad
		.group_size	= fixup_entry_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
	{
Packit Service ac8aad
		.name		= "__lwsync_fixup",
Packit Service ac8aad
		.group_size	= fixup_lwsync_group_size,
Packit Service ac8aad
	},
Packit Service ac8aad
#endif
Packit Service ac8aad
	{},
Packit Service ac8aad
};
Packit Service ac8aad
Packit Service ac8aad
static int should_keep_rela_group(struct section *sec, unsigned int start,
Packit Service ac8aad
		unsigned int size)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int found = 0;
Packit Service ac8aad
Packit Service ac8aad
	/* check if any relas in the group reference any changed functions */
Packit Service ac8aad
	list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
		if (rela->offset >= start &&
Packit Service ac8aad
		    rela->offset < start + size &&
Packit Service ac8aad
		    rela->sym->type == STT_FUNC &&
Packit Service ac8aad
		    rela->sym->sec->include) {
Packit Service ac8aad
			found = 1;
Packit Service ac8aad
			log_debug("new/changed symbol %s found in special section %s\n",
Packit Service ac8aad
				  rela->sym->name, sec->name);
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return found;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * When updating .fixup, the corresponding addends in .ex_table need to be
Packit Service ac8aad
 * updated too. Stash the result in rela.r_addend so that the calculation in
Packit Service ac8aad
 * fixup_group_size() is not affected.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf,
Packit Service ac8aad
					  struct special_section *special,
Packit Service ac8aad
					  int src_offset, int dest_offset,
Packit Service ac8aad
					  int group_size)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
Packit Service ac8aad
	sec = find_section_by_name(&kelf->sections, ".rela__ex_table");
Packit Service ac8aad
	if (!sec)
Packit Service ac8aad
		ERROR("missing .rela__ex_table section");
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
		if (!strcmp(rela->sym->name, ".fixup") &&
Packit Service ac8aad
		    rela->addend >= src_offset &&
Packit Service ac8aad
		    rela->addend < src_offset + group_size)
Packit Service ac8aad
			rela->rela.r_addend = rela->addend - (src_offset - dest_offset);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
Packit Service ac8aad
					      struct special_section *special,
Packit Service ac8aad
					      struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela, *safe;
Packit Service ac8aad
	char *src, *dest;
Packit Service ac8aad
	unsigned int group_size, src_offset, dest_offset, include;
Packit Service ac8aad
Packit Service ac8aad
	LIST_HEAD(newrelas);
Packit Service ac8aad
Packit Service ac8aad
	src = sec->base->data->d_buf;
Packit Service ac8aad
	/* alloc buffer for new base section */
Packit Service ac8aad
	dest = malloc(sec->base->sh.sh_size);
Packit Service ac8aad
	if (!dest)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
Packit Service ac8aad
	/* Restore the stashed r_addend from kpatch_update_ex_table_addend. */
Packit Service ac8aad
	if (!strcmp(special->name, "__ex_table")) {
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
			if (!strcmp(rela->sym->name, ".fixup"))
Packit Service ac8aad
				rela->addend = rela->rela.r_addend;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	group_size = 0;
Packit Service ac8aad
	src_offset = 0;
Packit Service ac8aad
	dest_offset = 0;
Packit Service ac8aad
	for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) {
Packit Service ac8aad
Packit Service ac8aad
		group_size = special->group_size(kelf, src_offset);
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * In some cases the struct has padding at the end to ensure
Packit Service ac8aad
		 * that all structs after it are properly aligned.  But the
Packit Service ac8aad
		 * last struct in the section may not be padded.  In that case,
Packit Service ac8aad
		 * shrink the group_size such that it still (hopefully)
Packit Service ac8aad
		 * contains the data but doesn't go past the end of the
Packit Service ac8aad
		 * section.
Packit Service ac8aad
		 */
Packit Service ac8aad
		if (src_offset + group_size > sec->base->sh.sh_size)
Packit Service ac8aad
			group_size = sec->base->sh.sh_size - src_offset;
Packit Service ac8aad
Packit Service ac8aad
		include = should_keep_rela_group(sec, src_offset, group_size);
Packit Service ac8aad
Packit Service ac8aad
		if (!include)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Copy all relas in the group.  It's possible that the relas
Packit Service ac8aad
		 * aren't sorted (e.g. .rela.fixup), so go through the entire
Packit Service ac8aad
		 * rela list each time.
Packit Service ac8aad
		 */
Packit Service ac8aad
		list_for_each_entry_safe(rela, safe, &sec->relas, list) {
Packit Service ac8aad
			if (rela->offset >= src_offset &&
Packit Service ac8aad
			    rela->offset < src_offset + group_size) {
Packit Service ac8aad
				/* copy rela entry */
Packit Service ac8aad
				list_del(&rela->list);
Packit Service ac8aad
				list_add_tail(&rela->list, &newrelas);
Packit Service ac8aad
Packit Service ac8aad
				rela->offset -= src_offset - dest_offset;
Packit Service ac8aad
				rela->rela.r_offset = rela->offset;
Packit Service ac8aad
Packit Service ac8aad
				rela->sym->include = 1;
Packit Service ac8aad
Packit Service ac8aad
Packit Service ac8aad
				if (!strcmp(special->name, ".fixup"))
Packit Service ac8aad
					kpatch_update_ex_table_addend(kelf, special,
Packit Service ac8aad
								      src_offset,
Packit Service ac8aad
								      dest_offset,
Packit Service ac8aad
								      group_size);
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		/* copy base section group */
Packit Service ac8aad
		memcpy(dest + dest_offset, src + src_offset, group_size);
Packit Service ac8aad
		dest_offset += group_size;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!dest_offset) {
Packit Service ac8aad
		/* no changed or global functions referenced */
Packit Service ac8aad
		sec->status = sec->base->status = SAME;
Packit Service ac8aad
		sec->include = sec->base->include = 0;
Packit Service ac8aad
		free(dest);
Packit Service ac8aad
		return;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* overwrite with new relas list */
Packit Service ac8aad
	list_replace(&newrelas, &sec->relas);
Packit Service ac8aad
Packit Service ac8aad
	/* include both rela and base sections */
Packit Service ac8aad
	sec->include = 1;
Packit Service ac8aad
	sec->base->include = 1;
Packit Service ac8aad
	/* include secsym so .kpatch.arch relas can point to section symbols */
Packit Service ac8aad
	sec->base->secsym->include = 1;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Update text section data buf and size.
Packit Service ac8aad
	 *
Packit Service ac8aad
	 * The rela section's data buf and size will be regenerated in
Packit Service ac8aad
	 * kpatch_rebuild_rela_section_data().
Packit Service ac8aad
	 */
Packit Service ac8aad
	sec->base->data->d_buf = dest;
Packit Service ac8aad
	sec->base->data->d_size = dest_offset;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
#define ORC_IP_PTR_SIZE 4
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * This function is similar to kpatch_regenerate_special_section(), but
Packit Service ac8aad
 * customized for the ORC-related sections.  ORC is more special than the other
Packit Service ac8aad
 * special sections because each ORC entry is split into .orc_unwind (struct
Packit Service ac8aad
 * orc_entry) and .orc_unwind_ip.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_regenerate_orc_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela, *safe;
Packit Service ac8aad
	char *src, *dest, *str;
Packit Service ac8aad
	unsigned int src_idx = 0, dest_idx = 0, orc_entry_size;
Packit Service ac8aad
	struct section *orc_sec, *ip_sec;
Packit Service ac8aad
Packit Service ac8aad
Packit Service ac8aad
	str = getenv("ORC_STRUCT_SIZE");
Packit Service ac8aad
	if (!str)
Packit Service ac8aad
		return;
Packit Service ac8aad
	orc_entry_size = atoi(str);
Packit Service ac8aad
Packit Service ac8aad
	LIST_HEAD(newrelas);
Packit Service ac8aad
Packit Service ac8aad
	orc_sec = find_section_by_name(&kelf->sections, ".orc_unwind");
Packit Service ac8aad
	ip_sec  = find_section_by_name(&kelf->sections, ".orc_unwind_ip");
Packit Service ac8aad
Packit Service ac8aad
	if (!orc_sec || !ip_sec)
Packit Service ac8aad
		return;
Packit Service ac8aad
Packit Service ac8aad
	if (orc_sec->sh.sh_size % orc_entry_size != 0)
Packit Service ac8aad
		ERROR("bad .orc_unwind size");
Packit Service ac8aad
Packit Service ac8aad
	if (ip_sec->sh.sh_size !=
Packit Service ac8aad
	    (orc_sec->sh.sh_size / orc_entry_size) * ORC_IP_PTR_SIZE)
Packit Service ac8aad
		ERROR(".orc_unwind/.orc_unwind_ip size mismatch");
Packit Service ac8aad
Packit Service ac8aad
	src = orc_sec->data->d_buf;
Packit Service ac8aad
	dest = malloc(orc_sec->sh.sh_size);
Packit Service ac8aad
	if (!dest)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry_safe(rela, safe, &ip_sec->rela->relas, list) {
Packit Service ac8aad
Packit Service ac8aad
		if (rela->sym->type != STT_FUNC || !rela->sym->sec->include)
Packit Service ac8aad
			goto next;
Packit Service ac8aad
Packit Service ac8aad
		/* copy orc entry */
Packit Service ac8aad
		memcpy(dest + (dest_idx * orc_entry_size),
Packit Service ac8aad
		       src + (src_idx * orc_entry_size),
Packit Service ac8aad
		       orc_entry_size);
Packit Service ac8aad
Packit Service ac8aad
		/* move ip rela */
Packit Service ac8aad
		list_del(&rela->list);
Packit Service ac8aad
		list_add_tail(&rela->list, &newrelas);
Packit Service ac8aad
		rela->offset = dest_idx * ORC_IP_PTR_SIZE;
Packit Service ac8aad
		rela->sym->include = 1;
Packit Service ac8aad
Packit Service ac8aad
		dest_idx++;
Packit Service ac8aad
next:
Packit Service ac8aad
		src_idx++;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!dest_idx) {
Packit Service ac8aad
		/* no changed or global functions referenced */
Packit Service ac8aad
		orc_sec->status = ip_sec->status = ip_sec->rela->status = SAME;
Packit Service ac8aad
		orc_sec->include = ip_sec->include = ip_sec->rela->include = 0;
Packit Service ac8aad
		free(dest);
Packit Service ac8aad
		return;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* overwrite with new relas list */
Packit Service ac8aad
	list_replace(&newrelas, &ip_sec->rela->relas);
Packit Service ac8aad
Packit Service ac8aad
	/* include the sections */
Packit Service ac8aad
	orc_sec->include = ip_sec->include = ip_sec->rela->include = 1;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Update data buf/size.
Packit Service ac8aad
	 *
Packit Service ac8aad
	 * The ip section can keep its old (zeroed data), though its size has
Packit Service ac8aad
	 * possibly decreased.  The ip rela section's data buf and size will be
Packit Service ac8aad
	 * regenerated in kpatch_rebuild_rela_section_data().
Packit Service ac8aad
	 */
Packit Service ac8aad
	orc_sec->data->d_buf = dest;
Packit Service ac8aad
	orc_sec->data->d_size = dest_idx * orc_entry_size;
Packit Service ac8aad
	ip_sec->data->d_size = dest_idx * ORC_IP_PTR_SIZE;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_check_relocations(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	Elf_Data *sdata;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!is_rela_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
			if (rela->sym->sec) {
Packit Service ac8aad
				sdata = rela->sym->sec->data;
Packit Service ac8aad
				if (rela->addend > (int)sdata->d_size) {
Packit Service ac8aad
					ERROR("out-of-range relocation %s+%x in %s", rela->sym->sec->name,
Packit Service ac8aad
							rela->addend, sec->name);
Packit Service ac8aad
				}
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_include_debug_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct rela *rela, *saferela;
Packit Service ac8aad
Packit Service ac8aad
	/* include all .debug_* sections */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (is_debug_section(sec)) {
Packit Service ac8aad
			sec->include = 1;
Packit Service ac8aad
			if (!is_rela_section(sec))
Packit Service ac8aad
				sec->secsym->include = 1;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Go through the .rela.debug_ sections and strip entries
Packit Service ac8aad
	 * referencing unchanged symbols
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!is_rela_section(sec) || !is_debug_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		list_for_each_entry_safe(rela, saferela, &sec->relas, list)
Packit Service ac8aad
			if (!rela->sym->sec->include)
Packit Service ac8aad
				list_del(&rela->list);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec, *strsec, *ignoresec;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	char *name;
Packit Service ac8aad
Packit Service ac8aad
	/* Ignore any discarded sections */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!strncmp(sec->name, ".discard", 8) ||
Packit Service ac8aad
		    !strncmp(sec->name, ".rela.discard", 13))
Packit Service ac8aad
			sec->ignore = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.sections");
Packit Service ac8aad
	if (!sec)
Packit Service ac8aad
		return;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(rela, &sec->rela->relas, list) {
Packit Service ac8aad
		strsec = rela->sym->sec;
Packit Service ac8aad
		strsec->status = CHANGED;
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Include the string section here.  This is because the
Packit Service ac8aad
		 * KPATCH_IGNORE_SECTION() macro is passed a literal string
Packit Service ac8aad
		 * by the patch author, resulting in a change to the string
Packit Service ac8aad
		 * section.  If we don't include it, then we will potentially
Packit Service ac8aad
		 * get a "changed section not included" error in
Packit Service ac8aad
		 * kpatch_verify_patchability() if no other function based change
Packit Service ac8aad
		 * also changes the string section.  We could try to exclude each
Packit Service ac8aad
		 * literal string added to the section by KPATCH_IGNORE_SECTION()
Packit Service ac8aad
		 * from the section data comparison, but this is a simpler way.
Packit Service ac8aad
		 */
Packit Service ac8aad
		strsec->include = 1;
Packit Service ac8aad
		name = strsec->data->d_buf + rela->addend;
Packit Service ac8aad
		ignoresec = find_section_by_name(&kelf->sections, name);
Packit Service ac8aad
		if (!ignoresec)
Packit Service ac8aad
			ERROR("KPATCH_IGNORE_SECTION: can't find %s", name);
Packit Service ac8aad
		log_normal("ignoring section: %s\n", name);
Packit Service ac8aad
		if (is_rela_section(ignoresec))
Packit Service ac8aad
			ignoresec = ignoresec->base;
Packit Service ac8aad
		ignoresec->ignore = 1;
Packit Service ac8aad
		if (ignoresec->twin)
Packit Service ac8aad
			ignoresec->twin->ignore = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_mark_ignored_sections_same(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!sec->ignore)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		sec->status = SAME;
Packit Service ac8aad
		if (!is_rela_section(sec)) {
Packit Service ac8aad
			if (sec->secsym)
Packit Service ac8aad
				sec->secsym->status = SAME;
Packit Service ac8aad
			if (sec->rela)
Packit Service ac8aad
				sec->rela->status = SAME;
Packit Service ac8aad
		}
Packit Service ac8aad
		list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
			if (sym->sec != sec)
Packit Service ac8aad
				continue;
Packit Service ac8aad
			sym->status = SAME;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* strip temporary global __UNIQUE_ID_kpatch_ignore_section_* symbols */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
		if (!strncmp(sym->name, "__UNIQUE_ID_kpatch_ignore_section_",
Packit Service ac8aad
		            strlen("__UNIQUE_ID_kpatch_ignore_section_")))
Packit Service ac8aad
			sym->status = SAME;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
Packit Service ac8aad
	sec = find_section_by_name(&kelf->sections, ".kpatch.ignore.functions");
Packit Service ac8aad
	if (!sec)
Packit Service ac8aad
		return;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(rela, &sec->rela->relas, list) {
Packit Service ac8aad
		if (!rela->sym->sec)
Packit Service ac8aad
			ERROR("expected bundled symbol");
Packit Service ac8aad
		if (rela->sym->type != STT_FUNC)
Packit Service ac8aad
			ERROR("expected function symbol");
Packit Service ac8aad
		log_normal("ignoring function: %s\n", rela->sym->name);
Packit Service ac8aad
		if (rela->sym->status != CHANGED)
Packit Service ac8aad
			log_normal("NOTICE: no change detected in function %s, unnecessary KPATCH_IGNORE_FUNCTION()?\n", rela->sym->name);
Packit Service ac8aad
		rela->sym->status = SAME;
Packit Service ac8aad
		rela->sym->sec->status = SAME;
Packit Service ac8aad
		if (rela->sym->sec->secsym)
Packit Service ac8aad
			rela->sym->sec->secsym->status = SAME;
Packit Service ac8aad
		if (rela->sym->sec->rela)
Packit Service ac8aad
			rela->sym->sec->rela->status = SAME;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* strip temporary global kpatch_ignore_func_* symbols */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
		if (!strncmp(sym->name, "__kpatch_ignore_func_",
Packit Service ac8aad
		            strlen("__kpatch_ignore_func_")))
Packit Service ac8aad
			sym->status = SAME;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *objname)
Packit Service ac8aad
{
Packit Service ac8aad
	struct special_section *special;
Packit Service ac8aad
	struct kpatch_arch *entries;
Packit Service ac8aad
	struct symbol *strsym;
Packit Service ac8aad
	struct section *sec, *karch_sec;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int nr, index = 0;
Packit Service ac8aad
Packit Service ac8aad
	nr = sizeof(special_sections) / sizeof(special_sections[0]);
Packit Service ac8aad
	karch_sec = create_section_pair(kelf, ".kpatch.arch", sizeof(*entries), nr);
Packit Service ac8aad
	entries = karch_sec->data->d_buf;
Packit Service ac8aad
Packit Service ac8aad
	/* lookup strings symbol */
Packit Service ac8aad
	strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
Packit Service ac8aad
	if (!strsym)
Packit Service ac8aad
		ERROR("can't find .kpatch.strings symbol");
Packit Service ac8aad
Packit Service ac8aad
	for (special = special_sections; special->name; special++) {
Packit Service ac8aad
		if (strcmp(special->name, ".parainstructions") &&
Packit Service ac8aad
		    strcmp(special->name, ".altinstructions"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sec = find_section_by_name(&kelf->sections, special->name);
Packit Service ac8aad
		if (!sec)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		/* entries[index].sec */
Packit Service ac8aad
		ALLOC_LINK(rela, &karch_sec->rela->relas);
Packit Service ac8aad
		rela->sym = sec->secsym;
Packit Service ac8aad
		rela->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
		rela->addend = 0;
Packit Service ac8aad
		rela->offset = index * sizeof(*entries) + \
Packit Service ac8aad
			       offsetof(struct kpatch_arch, sec);
Packit Service ac8aad
Packit Service ac8aad
		/* entries[index].objname */
Packit Service ac8aad
		ALLOC_LINK(rela, &karch_sec->rela->relas);
Packit Service ac8aad
		rela->sym = strsym;
Packit Service ac8aad
		rela->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
		rela->addend = offset_of_string(&kelf->strings, objname);
Packit Service ac8aad
		rela->offset = index * sizeof(*entries) + \
Packit Service ac8aad
			       offsetof(struct kpatch_arch, objname);
Packit Service ac8aad
Packit Service ac8aad
		index++;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	karch_sec->data->d_size = index * sizeof(struct kpatch_arch);
Packit Service ac8aad
	karch_sec->sh.sh_size = karch_sec->data->d_size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_process_special_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct special_section *special;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int altinstr = 0;
Packit Service ac8aad
Packit Service ac8aad
	for (special = special_sections; special->name; special++) {
Packit Service ac8aad
		sec = find_section_by_name(&kelf->sections, special->name);
Packit Service ac8aad
		if (!sec)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sec = sec->rela;
Packit Service ac8aad
		if (!sec)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		kpatch_regenerate_special_section(kelf, special, sec);
Packit Service ac8aad
Packit Service ac8aad
		if (!strcmp(special->name, ".altinstructions") && sec->base->include)
Packit Service ac8aad
			altinstr = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * The following special sections don't have relas which reference
Packit Service ac8aad
	 * non-included symbols, so their entire rela section can be included.
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (strcmp(sec->name, ".altinstr_replacement"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Only include .altinstr_replacement if .altinstructions
Packit Service ac8aad
		 * is also included.
Packit Service ac8aad
		 */
Packit Service ac8aad
		if (!altinstr)
Packit Service ac8aad
			break;
Packit Service ac8aad
Packit Service ac8aad
		/* include base section */
Packit Service ac8aad
		sec->include = 1;
Packit Service ac8aad
Packit Service ac8aad
		/* include all symbols in the section */
Packit Service ac8aad
		list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
			if (sym->sec == sec)
Packit Service ac8aad
				sym->include = 1;
Packit Service ac8aad
Packit Service ac8aad
		/* include rela section */
Packit Service ac8aad
		if (sec->rela) {
Packit Service ac8aad
			sec->rela->include = 1;
Packit Service ac8aad
			/* include all symbols referenced by relas */
Packit Service ac8aad
			list_for_each_entry(rela, &sec->rela->relas, list)
Packit Service ac8aad
				rela->sym->include = 1;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * The following special sections aren't supported, so make sure we
Packit Service ac8aad
	 * don't ever try to include them.  Otherwise the kernel will see the
Packit Service ac8aad
	 * jump table during module loading and get confused.  Generally it
Packit Service ac8aad
	 * should be safe to exclude them, it just means that you can't modify
Packit Service ac8aad
	 * jump labels and enable tracepoints in a patched function.
Packit Service ac8aad
	 */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (strcmp(sec->name, "__jump_table") &&
Packit Service ac8aad
		    strcmp(sec->name, "__tracepoints") &&
Packit Service ac8aad
		    strcmp(sec->name, "__tracepoints_ptrs") &&
Packit Service ac8aad
		    strcmp(sec->name, "__tracepoints_strings"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sec->status = SAME;
Packit Service ac8aad
		sec->include = 0;
Packit Service ac8aad
		if (sec->rela) {
Packit Service ac8aad
			sec->rela->status = SAME;
Packit Service ac8aad
			sec->rela->include = 0;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	kpatch_regenerate_orc_sections(kelf);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static struct sym_compare_type *kpatch_elf_locals(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	int i = 0, sym_num = 0;
Packit Service ac8aad
	struct sym_compare_type *sym_array;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->bind != STB_LOCAL)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		if (sym->type != STT_FUNC && sym->type != STT_OBJECT)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sym_num++;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!sym_num)
Packit Service ac8aad
		return NULL;
Packit Service ac8aad
Packit Service ac8aad
	sym_array = malloc((sym_num + 1) * sizeof(struct sym_compare_type));
Packit Service ac8aad
	if (!sym_array)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->bind != STB_LOCAL)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		if (sym->type != STT_FUNC && sym->type != STT_OBJECT)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sym_array[i].type = sym->type;
Packit Service ac8aad
		sym_array[i++].name = sym->name;
Packit Service ac8aad
	}
Packit Service ac8aad
	sym_array[i].type = 0;
Packit Service ac8aad
	sym_array[i].name = NULL;
Packit Service ac8aad
Packit Service ac8aad
	return sym_array;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
Packit Service ac8aad
					   struct lookup_table *table,
Packit Service ac8aad
					   char *objname)
Packit Service ac8aad
{
Packit Service ac8aad
	int nr, index, objname_offset;
Packit Service ac8aad
	struct section *sec, *relasec;
Packit Service ac8aad
	struct symbol *sym, *strsym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	struct lookup_result result;
Packit Service ac8aad
	struct kpatch_patch_func *funcs;
Packit Service ac8aad
Packit Service ac8aad
	/* count patched functions */
Packit Service ac8aad
	nr = 0;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
		if (sym->type == STT_FUNC && sym->status == CHANGED)
Packit Service ac8aad
			nr++;
Packit Service ac8aad
Packit Service ac8aad
	/* create text/rela section pair */
Packit Service ac8aad
	sec = create_section_pair(kelf, ".kpatch.funcs", sizeof(*funcs), nr);
Packit Service ac8aad
	relasec = sec->rela;
Packit Service ac8aad
	funcs = sec->data->d_buf;
Packit Service ac8aad
Packit Service ac8aad
	/* lookup strings symbol */
Packit Service ac8aad
	strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
Packit Service ac8aad
	if (!strsym)
Packit Service ac8aad
		ERROR("can't find .kpatch.strings symbol");
Packit Service ac8aad
Packit Service ac8aad
	/* add objname to strings */
Packit Service ac8aad
	objname_offset = offset_of_string(&kelf->strings, objname);
Packit Service ac8aad
Packit Service ac8aad
	/* populate sections */
Packit Service ac8aad
	index = 0;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->type == STT_FUNC && sym->status == CHANGED) {
Packit Service ac8aad
			if (sym->bind == STB_LOCAL) {
Packit Service ac8aad
				if (lookup_local_symbol(table, sym->name,
Packit Service ac8aad
				                        &result))
Packit Service ac8aad
					ERROR("lookup_local_symbol %s",
Packit Service ac8aad
					      sym->name);
Packit Service ac8aad
			} else {
Packit Service ac8aad
				if(lookup_global_symbol(table, sym->name,
Packit Service ac8aad
				                        &result))
Packit Service ac8aad
					ERROR("lookup_global_symbol %s",
Packit Service ac8aad
					      sym->name);
Packit Service ac8aad
			}
Packit Service ac8aad
			log_debug("lookup for %s @ 0x%016lx len %lu\n",
Packit Service ac8aad
			          sym->name, result.value, result.size);
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Convert global symbols to local so other objects in
Packit Service ac8aad
			 * the patch module (like the patch callback object's init
Packit Service ac8aad
			 * code) won't link to this function and call it before
Packit Service ac8aad
			 * its relocations have been applied.
Packit Service ac8aad
			 */
Packit Service ac8aad
			sym->bind = STB_LOCAL;
Packit Service ac8aad
			sym->sym.st_info = GELF_ST_INFO(sym->bind, sym->type);
Packit Service ac8aad
Packit Service ac8aad
			/* add entry in text section */
Packit Service ac8aad
			funcs[index].old_addr = result.value;
Packit Service ac8aad
			funcs[index].old_size = result.size;
Packit Service ac8aad
			funcs[index].new_size = sym->sym.st_size;
Packit Service ac8aad
			funcs[index].sympos = result.pos;
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Add a relocation that will populate
Packit Service ac8aad
			 * the funcs[index].new_addr field at
Packit Service ac8aad
			 * module load time.
Packit Service ac8aad
			 */
Packit Service ac8aad
			ALLOC_LINK(rela, &relasec->relas);
Packit Service ac8aad
			rela->sym = sym;
Packit Service ac8aad
			rela->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela->addend = 0;
Packit Service ac8aad
			rela->offset = index * sizeof(*funcs);
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Add a relocation that will populate
Packit Service ac8aad
			 * the funcs[index].name field.
Packit Service ac8aad
			 */
Packit Service ac8aad
			ALLOC_LINK(rela, &relasec->relas);
Packit Service ac8aad
			rela->sym = strsym;
Packit Service ac8aad
			rela->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela->addend = offset_of_string(&kelf->strings, sym->name);
Packit Service ac8aad
			rela->offset = index * sizeof(*funcs) +
Packit Service ac8aad
			               offsetof(struct kpatch_patch_func, name);
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Add a relocation that will populate
Packit Service ac8aad
			 * the funcs[index].objname field.
Packit Service ac8aad
			 */
Packit Service ac8aad
			ALLOC_LINK(rela, &relasec->relas);
Packit Service ac8aad
			rela->sym = strsym;
Packit Service ac8aad
			rela->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela->addend = objname_offset;
Packit Service ac8aad
			rela->offset = index * sizeof(*funcs) +
Packit Service ac8aad
			               offsetof(struct kpatch_patch_func,objname);
Packit Service ac8aad
Packit Service ac8aad
			index++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* sanity check, index should equal nr */
Packit Service ac8aad
	if (index != nr)
Packit Service ac8aad
		ERROR("size mismatch in funcs sections");
Packit Service ac8aad
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int kpatch_is_core_module_symbol(char *name)
Packit Service ac8aad
{
Packit Service ac8aad
	return (!strcmp(name, "kpatch_shadow_alloc") ||
Packit Service ac8aad
		!strcmp(name, "kpatch_shadow_free") ||
Packit Service ac8aad
		!strcmp(name, "kpatch_shadow_get"));
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * If the patched code refers to a symbol, for example, calls a function
Packit Service ac8aad
 * or stores a pointer to a function somewhere, the address of that symbol
Packit Service ac8aad
 * must be resolved somehow before the patch is applied. The symbol may be
Packit Service ac8aad
 * present in the original code too, so the patch may refer either to that
Packit Service ac8aad
 * version of the symbol (dynrela is used for that) or to its patched
Packit Service ac8aad
 * version directly (with a normal relocation).
Packit Service ac8aad
 *
Packit Service ac8aad
 * Dynrelas may be needed for the symbols not present in this object file
Packit Service ac8aad
 * (rela->sym->sec is NULL), because it is unknown if the patched versions
Packit Service ac8aad
 * of these symbols exist and where they are.
Packit Service ac8aad
 *
Packit Service ac8aad
 * The patched code can usually refer to a symbol from this object file
Packit Service ac8aad
 * directly. If it is a function, this may also improve performance because
Packit Service ac8aad
 * it will not be needed to call the original function first, find the
Packit Service ac8aad
 * patched one and then use Ftrace to pass control to it.
Packit Service ac8aad
 *
Packit Service ac8aad
 * There is an exception though, at least on x86. It is safer to use
Packit Service ac8aad
 * a dynrela if the patched code stores a pointer to a function somewhere
Packit Service ac8aad
 * (relocation of type R_X86_64_32S). The function could be used as
Packit Service ac8aad
 * a callback and some kinds of callbacks are called asynchronously. If
Packit Service ac8aad
 * the patch module sets such callback and is unloaded shortly after,
Packit Service ac8aad
 * the kernel could try to call the function via an invalid pointer and
Packit Service ac8aad
 * would crash. With dynrela, the kernel would call the original function
Packit Service ac8aad
 * in that case.
Packit Service ac8aad
 */
Packit Service ac8aad
static int function_ptr_rela(const struct rela *rela)
Packit Service ac8aad
{
Packit Service ac8aad
	const struct rela *rela_toc = toc_rela(rela);
Packit Service ac8aad
Packit Service ac8aad
	return (rela_toc && rela_toc->sym->type == STT_FUNC &&
Packit Service ac8aad
		/* skip switch table on PowerPC */
Packit Service ac8aad
		rela_toc->addend == (int)rela_toc->sym->sym.st_value &&
Packit Service ac8aad
		(rela->type == R_X86_64_32S ||
Packit Service ac8aad
		rela->type == R_PPC64_TOC16_HA ||
Packit Service ac8aad
		rela->type == R_PPC64_TOC16_LO_DS));
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static int may_need_dynrela(const struct rela *rela)
Packit Service ac8aad
{
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * References to .TOC. are treated specially by the module loader and
Packit Service ac8aad
	 * should never be converted to dynrelas.
Packit Service ac8aad
	 */
Packit Service ac8aad
	if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO ||
Packit Service ac8aad
	    rela->type == R_PPC64_REL64)
Packit Service ac8aad
		return 0;
Packit Service ac8aad
Packit Service ac8aad
	if (!rela->sym->sec)
Packit Service ac8aad
		return 1;
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Nested functions used as callbacks are a special case.
Packit Service ac8aad
	 * They are not supposed to be visible outside of the
Packit Service ac8aad
	 * function that defines them.  Their names may differ in
Packit Service ac8aad
	 * the original and the patched kernels which makes it
Packit Service ac8aad
	 * difficult to use dynrelas.  Fortunately, nested functions
Packit Service ac8aad
	 * are rare and are unlikely to be used as asynchronous
Packit Service ac8aad
	 * callbacks, so the patched code can refer to them directly.
Packit Service ac8aad
	 * It seems, one can only distinguish such functions by their
Packit Service ac8aad
	 * names containing a dot.  Other kinds of functions with
Packit Service ac8aad
	 * such names (e.g. optimized copies of functions) are
Packit Service ac8aad
	 * unlikely to be used as callbacks.
Packit Service ac8aad
	 */
Packit Service ac8aad
	return (function_ptr_rela(rela) &&
Packit Service ac8aad
		toc_rela(rela)->sym->status != NEW &&
Packit Service ac8aad
		!strchr(toc_rela(rela)->sym->name, '.'));
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
Packit Service ac8aad
						struct lookup_table *table,
Packit Service ac8aad
						char *objname,
Packit Service ac8aad
						char *pmod_name)
Packit Service ac8aad
{
Packit Service ac8aad
	int nr, index;
Packit Service ac8aad
	struct section *sec, *ksym_sec, *krela_sec;
Packit Service ac8aad
	struct rela *rela, *rela2, *safe;
Packit Service ac8aad
	struct symbol *strsym, *ksym_sec_sym;
Packit Service ac8aad
	struct kpatch_symbol *ksyms;
Packit Service ac8aad
	struct kpatch_relocation *krelas;
Packit Service ac8aad
	struct lookup_result result;
Packit Service ac8aad
	char *sym_objname;
Packit Service ac8aad
	int ret, vmlinux, external;
Packit Service ac8aad
Packit Service ac8aad
	vmlinux = !strcmp(objname, "vmlinux");
Packit Service ac8aad
Packit Service ac8aad
	/* count rela entries that need to be dynamic */
Packit Service ac8aad
	nr = 0;
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!is_rela_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		if (!strcmp(sec->name, ".rela.kpatch.funcs"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
			nr++; /* upper bound on number of kpatch relas and symbols */
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries:
Packit Service ac8aad
			 * ...
Packit Service ac8aad
			 * 0000000000000138  0000002a00000026 R_PPC64_ADDR64      0000000000000000 .text.deferred_put_nlk_sk + 8
Packit Service ac8aad
			 *
Packit Service ac8aad
			 * Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries:
Packit Service ac8aad
			 * ...
Packit Service ac8aad
			 * 0000000000000398  0000007300000032 R_PPC64_TOC16_HA    0000000000000000 .toc + 138
Packit Service ac8aad
			 * 00000000000003a0  0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138
Packit Service ac8aad
			 *
Packit Service ac8aad
			 * On PowerPC, may_need_dynrela() should be using rela's reference in .rela.toc for
Packit Service ac8aad
			 * the rela like in the example, where the sym name is .toc + offset. In such case,
Packit Service ac8aad
			 * the checks are performed on both rela and its reference in .rela.toc. Where the
Packit Service ac8aad
			 * rela is checked for rela->type and its corresponding rela in .rela.toc for function
Packit Service ac8aad
			 * pointer/switch label. If rela->need_dynrela needs to be set, it's referenced rela
Packit Service ac8aad
			 * in (.rela.toc)->need_dynrela is set, as they represent the function sym.
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (may_need_dynrela(rela))
Packit Service ac8aad
				toc_rela(rela)->need_dynrela = 1;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* create .kpatch.relocations text/rela section pair */
Packit Service ac8aad
	krela_sec = create_section_pair(kelf, ".kpatch.relocations", sizeof(*krelas), nr);
Packit Service ac8aad
	krelas = krela_sec->data->d_buf;
Packit Service ac8aad
Packit Service ac8aad
	/* create .kpatch.symbols text/rela section pair */
Packit Service ac8aad
	ksym_sec = create_section_pair(kelf, ".kpatch.symbols", sizeof(*ksyms), nr);
Packit Service ac8aad
	ksyms = ksym_sec->data->d_buf;
Packit Service ac8aad
Packit Service ac8aad
	/* create .kpatch.symbols section symbol (to set rela->sym later) */
Packit Service ac8aad
	ALLOC_LINK(ksym_sec_sym, &kelf->symbols);
Packit Service ac8aad
	ksym_sec_sym->sec = ksym_sec;
Packit Service ac8aad
	ksym_sec_sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
Packit Service ac8aad
	ksym_sec_sym->type = STT_SECTION;
Packit Service ac8aad
	ksym_sec_sym->bind = STB_LOCAL;
Packit Service ac8aad
	ksym_sec_sym->name = ".kpatch.symbols";
Packit Service ac8aad
Packit Service ac8aad
	/* lookup strings symbol */
Packit Service ac8aad
	strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
Packit Service ac8aad
	if (!strsym)
Packit Service ac8aad
		ERROR("can't find .kpatch.strings symbol");
Packit Service ac8aad
Packit Service ac8aad
	/* populate sections */
Packit Service ac8aad
	index = 0;
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		if (!is_rela_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		if (!strcmp(sec->name, ".rela.kpatch.funcs") ||
Packit Service ac8aad
		    !strcmp(sec->name, ".rela.kpatch.dynrelas"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		list_for_each_entry_safe(rela, safe, &sec->relas, list) {
Packit Service ac8aad
			if (!rela->need_dynrela)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Allow references to core module symbols to remain as
Packit Service ac8aad
			 * normal relas, since the core module may not be
Packit Service ac8aad
			 * compiled into the kernel, and they should be
Packit Service ac8aad
			 * exported anyway.
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (kpatch_is_core_module_symbol(rela->sym->name))
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			external = 0;
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * sym_objname is the name of the object to which
Packit Service ac8aad
			 * rela->sym belongs. We'll need this to build
Packit Service ac8aad
			 * ".klp.sym." symbol names later on.
Packit Service ac8aad
			 *
Packit Service ac8aad
			 * By default sym_objname is the name of the
Packit Service ac8aad
			 * component being patched (vmlinux or module).
Packit Service ac8aad
			 * If it's an external symbol, sym_objname
Packit Service ac8aad
			 * will get reassigned appropriately.
Packit Service ac8aad
			 */
Packit Service ac8aad
			sym_objname = objname;
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * On ppc64le, the function prologue generated by GCC 6
Packit Service ac8aad
			 * has the sequence:
Packit Service ac8aad
			 *
Packit Service ac8aad
			 *	.globl my_func
Packit Service ac8aad
			 *	.type my_func, @function
Packit Service ac8aad
			 *	.quad .TOC.-my_func
Packit Service ac8aad
			 * my_func:
Packit Service ac8aad
			 *	.reloc ., R_PPC64_ENTRY ; optional
Packit Service ac8aad
			 *	ld r2,-8(r12)
Packit Service ac8aad
			 *	add r2,r2,r12
Packit Service ac8aad
			 *	.localentry my_func, .-my_func
Packit Service ac8aad
			 *
Packit Service ac8aad
			 * The R_PPC64_ENTRY is optional and its symbol might
Packit Service ac8aad
			 * have an empty name.  Leave it as a normal rela.
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (rela->type == R_PPC64_ENTRY)
Packit Service ac8aad
				continue;
Packit Service ac8aad
Packit Service ac8aad
			if (rela->sym->bind == STB_LOCAL) {
Packit Service ac8aad
				/* An unchanged local symbol */
Packit Service ac8aad
				ret = lookup_local_symbol(table,
Packit Service ac8aad
					rela->sym->name, &result);
Packit Service ac8aad
				if (ret)
Packit Service ac8aad
					ERROR("lookup_local_symbol %s needed for %s",
Packit Service ac8aad
					      rela->sym->name, sec->base->name);
Packit Service ac8aad
Packit Service ac8aad
			}
Packit Service ac8aad
			else if (vmlinux) {
Packit Service ac8aad
				/*
Packit Service ac8aad
				 * We have a patch to vmlinux which references
Packit Service ac8aad
				 * a global symbol.  Use a normal rela for
Packit Service ac8aad
				 * exported symbols and a dynrela otherwise.
Packit Service ac8aad
				 */
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
				/*
Packit Service ac8aad
				 * An exported symbol might be local to an
Packit Service ac8aad
				 * object file and any access to the function
Packit Service ac8aad
				 * might be through localentry (toc+offset)
Packit Service ac8aad
				 * instead of global offset.
Packit Service ac8aad
				 *
Packit Service ac8aad
				 * fs/proc/proc_sysctl::sysctl_head_grab:
Packit Service ac8aad
				 * 166: 0000000000000000   256 FUNC    GLOBAL DEFAULT [<localentry>: 8]    42 unregister_sysctl_table
Packit Service ac8aad
				 * 167: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND .TOC.
Packit Service ac8aad
				 *
Packit Service ac8aad
				 * These type of symbols have a type of
Packit Service ac8aad
				 * STT_FUNC.  Treat them like local symbols.
Packit Service ac8aad
				 * They will be handled by the livepatch
Packit Service ac8aad
				 * relocation code.
Packit Service ac8aad
				 */
Packit Service ac8aad
				if (lookup_is_exported_symbol(table, rela->sym->name)) {
Packit Service ac8aad
					if (rela->sym->type != STT_FUNC)
Packit Service ac8aad
						continue;
Packit Service ac8aad
				}
Packit Service ac8aad
#else
Packit Service ac8aad
				if (lookup_is_exported_symbol(table, rela->sym->name))
Packit Service ac8aad
					continue;
Packit Service ac8aad
#endif
Packit Service ac8aad
				/*
Packit Service ac8aad
				 * If lookup_global_symbol() fails, assume the
Packit Service ac8aad
				 * symbol is defined in another object in the
Packit Service ac8aad
				 * patch module.
Packit Service ac8aad
				 */
Packit Service ac8aad
				if (lookup_global_symbol(table, rela->sym->name,
Packit Service ac8aad
							 &result))
Packit Service ac8aad
					continue;
Packit Service ac8aad
			} else {
Packit Service ac8aad
				/*
Packit Service ac8aad
				 * We have a patch to a module which references
Packit Service ac8aad
				 * a global symbol.  Try to find the symbol in
Packit Service ac8aad
				 * the module being patched.
Packit Service ac8aad
				 */
Packit Service ac8aad
				if (lookup_global_symbol(table, rela->sym->name,
Packit Service ac8aad
							 &result)) {
Packit Service ac8aad
					/*
Packit Service ac8aad
					 * Not there, see if the symbol is
Packit Service ac8aad
					 * exported, and set sym_objname to the
Packit Service ac8aad
					 * object the exported symbol belongs
Packit Service ac8aad
					 * to. If it's not exported, assume sym
Packit Service ac8aad
					 * is provided by another .o in the
Packit Service ac8aad
					 * patch module.
Packit Service ac8aad
					 */
Packit Service ac8aad
					sym_objname = lookup_exported_symbol_objname(table, rela->sym->name);
Packit Service ac8aad
					if (!sym_objname)
Packit Service ac8aad
						sym_objname = pmod_name;
Packit Service ac8aad
Packit Service ac8aad
					/*
Packit Service ac8aad
					 * For a symbol exported by vmlinux, use
Packit Service ac8aad
					 * the original rela.
Packit Service ac8aad
					 *
Packit Service ac8aad
					 * For a symbol exported by a module,
Packit Service ac8aad
					 * convert to a dynrela because the
Packit Service ac8aad
					 * module might not be loaded yet.
Packit Service ac8aad
					 */
Packit Service ac8aad
					if (!strcmp(sym_objname, "vmlinux"))
Packit Service ac8aad
						continue;
Packit Service ac8aad
Packit Service ac8aad
					external = 1;
Packit Service ac8aad
				}
Packit Service ac8aad
			}
Packit Service ac8aad
			log_debug("lookup for %s @ 0x%016lx len %lu\n",
Packit Service ac8aad
			          rela->sym->name, result.value, result.size);
Packit Service ac8aad
Packit Service ac8aad
			/* Fill in ksyms[index] */
Packit Service ac8aad
			if (vmlinux)
Packit Service ac8aad
				ksyms[index].src = result.value;
Packit Service ac8aad
			else
Packit Service ac8aad
				/* for modules, src is discovered at runtime */
Packit Service ac8aad
				ksyms[index].src = 0;
Packit Service ac8aad
			ksyms[index].pos = result.pos;
Packit Service ac8aad
			ksyms[index].type = rela->sym->type;
Packit Service ac8aad
			ksyms[index].bind = rela->sym->bind;
Packit Service ac8aad
Packit Service ac8aad
			/* add rela to fill in ksyms[index].name field */
Packit Service ac8aad
			ALLOC_LINK(rela2, &ksym_sec->rela->relas);
Packit Service ac8aad
			rela2->sym = strsym;
Packit Service ac8aad
			rela2->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
Packit Service ac8aad
			rela2->offset = index * sizeof(*ksyms) + \
Packit Service ac8aad
					offsetof(struct kpatch_symbol, name);
Packit Service ac8aad
Packit Service ac8aad
			/* add rela to fill in ksyms[index].objname field */
Packit Service ac8aad
			ALLOC_LINK(rela2, &ksym_sec->rela->relas);
Packit Service ac8aad
			rela2->sym = strsym;
Packit Service ac8aad
			rela2->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela2->addend = offset_of_string(&kelf->strings, sym_objname);
Packit Service ac8aad
			rela2->offset = index * sizeof(*ksyms) + \
Packit Service ac8aad
					offsetof(struct kpatch_symbol, objname);
Packit Service ac8aad
Packit Service ac8aad
			/* Fill in krelas[index] */
Packit Service ac8aad
			if (is_gcc6_localentry_bundled_sym(rela->sym) &&
Packit Service ac8aad
			    rela->addend == (int)rela->sym->sym.st_value)
Packit Service ac8aad
				rela->addend -= rela->sym->sym.st_value;
Packit Service ac8aad
			krelas[index].addend = rela->addend;
Packit Service ac8aad
			krelas[index].type = rela->type;
Packit Service ac8aad
			krelas[index].external = external;
Packit Service ac8aad
Packit Service ac8aad
			/* add rela to fill in krelas[index].dest field */
Packit Service ac8aad
			ALLOC_LINK(rela2, &krela_sec->rela->relas);
Packit Service ac8aad
			if (sec->base->secsym)
Packit Service ac8aad
				rela2->sym = sec->base->secsym;
Packit Service ac8aad
			else
Packit Service ac8aad
				ERROR("can't create dynrela for section %s (symbol %s): no bundled or section symbol",
Packit Service ac8aad
				      sec->name, rela->sym->name);
Packit Service ac8aad
Packit Service ac8aad
			rela2->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela2->addend = rela->offset;
Packit Service ac8aad
			rela2->offset = index * sizeof(*krelas) + \
Packit Service ac8aad
					offsetof(struct kpatch_relocation, dest);
Packit Service ac8aad
Packit Service ac8aad
			/* add rela to fill in krelas[index].objname field */
Packit Service ac8aad
			ALLOC_LINK(rela2, &krela_sec->rela->relas);
Packit Service ac8aad
			rela2->sym = strsym;
Packit Service ac8aad
			rela2->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela2->addend = offset_of_string(&kelf->strings, objname);
Packit Service ac8aad
			rela2->offset = index * sizeof(*krelas) + \
Packit Service ac8aad
				offsetof(struct kpatch_relocation, objname);
Packit Service ac8aad
Packit Service ac8aad
			/* add rela to fill in krelas[index].ksym field */
Packit Service ac8aad
			ALLOC_LINK(rela2, &krela_sec->rela->relas);
Packit Service ac8aad
			rela2->sym = ksym_sec_sym;
Packit Service ac8aad
			rela2->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
			rela2->addend = index * sizeof(*ksyms);
Packit Service ac8aad
			rela2->offset = index * sizeof(*krelas) + \
Packit Service ac8aad
				offsetof(struct kpatch_relocation, ksym);
Packit Service ac8aad
Packit Service ac8aad
			/*
Packit Service ac8aad
			 * Mark the referred to symbol for removal but
Packit Service ac8aad
			 * only if it is not from this object file.
Packit Service ac8aad
			 * The symbols from this object file may be needed
Packit Service ac8aad
			 * later (for example, they may have relocations
Packit Service ac8aad
			 * of their own which should be processed).
Packit Service ac8aad
			 */
Packit Service ac8aad
			if (!rela->sym->sec)
Packit Service ac8aad
				rela->sym->strip = 1;
Packit Service ac8aad
			list_del(&rela->list);
Packit Service ac8aad
			free(rela);
Packit Service ac8aad
Packit Service ac8aad
			index++;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* set size to actual number of ksyms/krelas */
Packit Service ac8aad
	ksym_sec->data->d_size = index * sizeof(struct kpatch_symbol);
Packit Service ac8aad
	ksym_sec->sh.sh_size = ksym_sec->data->d_size;
Packit Service ac8aad
Packit Service ac8aad
	krela_sec->data->d_size = index * sizeof(struct kpatch_relocation);
Packit Service ac8aad
	krela_sec->sh.sh_size = krela_sec->data->d_size;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char *objname)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	struct symbol *strsym;
Packit Service ac8aad
	int objname_offset;
Packit Service ac8aad
Packit Service ac8aad
	struct callback { char *name; int offset; };
Packit Service ac8aad
	static struct callback callbacks[] = {
Packit Service ac8aad
		{ .name = ".rela.kpatch.callbacks.pre_patch",
Packit Service ac8aad
		  .offset = offsetof(struct kpatch_pre_patch_callback, objname) },
Packit Service ac8aad
		{ .name = ".rela.kpatch.callbacks.post_patch",
Packit Service ac8aad
		  .offset = offsetof(struct kpatch_post_patch_callback, objname) },
Packit Service ac8aad
		{ .name = ".rela.kpatch.callbacks.pre_unpatch",
Packit Service ac8aad
		  .offset = offsetof(struct kpatch_pre_unpatch_callback, objname) },
Packit Service ac8aad
		{ .name = ".rela.kpatch.callbacks.post_unpatch",
Packit Service ac8aad
		  .offset = offsetof(struct kpatch_post_patch_callback, objname) },
Packit Service ac8aad
		{ .name = NULL, .offset = 0 },
Packit Service ac8aad
	};
Packit Service ac8aad
	struct callback *callbackp;
Packit Service ac8aad
Packit Service ac8aad
	/* lookup strings symbol */
Packit Service ac8aad
	strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings");
Packit Service ac8aad
	if (!strsym)
Packit Service ac8aad
		ERROR("can't find .kpatch.strings symbol");
Packit Service ac8aad
Packit Service ac8aad
	/* add objname to strings */
Packit Service ac8aad
	objname_offset = offset_of_string(&kelf->strings, objname);
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		for (callbackp = callbacks; callbackp->name; callbackp++) {
Packit Service ac8aad
			if (!strcmp(callbackp->name, sec->name)) {
Packit Service ac8aad
				ALLOC_LINK(rela, &sec->relas);
Packit Service ac8aad
				rela->sym = strsym;
Packit Service ac8aad
				rela->type = ABSOLUTE_RELA_TYPE;
Packit Service ac8aad
				rela->addend = objname_offset;
Packit Service ac8aad
				rela->offset = callbackp->offset;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
void kpatch_create_mcount_sections(struct kpatch_elf *kelf) { }
Packit Service ac8aad
#else
Packit Service ac8aad
/*
Packit Service ac8aad
 * This function basically reimplements the functionality of the Linux
Packit Service ac8aad
 * recordmcount script, so that patched functions can be recognized by ftrace.
Packit Service ac8aad
 *
Packit Service ac8aad
 * TODO: Eventually we can modify recordmount so that it recognizes our bundled
Packit Service ac8aad
 * sections as valid and does this work for us.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	int nr, index;
Packit Service ac8aad
	struct section *sec, *relasec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	void **funcs, *newdata;
Packit Service ac8aad
	unsigned char *insn;
Packit Service ac8aad
Packit Service ac8aad
	nr = 0;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
		if (sym->type == STT_FUNC && sym->status != SAME &&
Packit Service ac8aad
		    sym->has_func_profiling)
Packit Service ac8aad
			nr++;
Packit Service ac8aad
Packit Service ac8aad
	/* create text/rela section pair */
Packit Service ac8aad
	sec = create_section_pair(kelf, "__mcount_loc", sizeof(*funcs), nr);
Packit Service ac8aad
	relasec = sec->rela;
Packit Service ac8aad
	funcs = sec->data->d_buf;
Packit Service ac8aad
Packit Service ac8aad
	/* populate sections */
Packit Service ac8aad
	index = 0;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->type != STT_FUNC || sym->status == SAME)
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		if (!sym->has_func_profiling) {
Packit Service ac8aad
			log_debug("function %s has no fentry/mcount call, no mcount record is needed\n",
Packit Service ac8aad
				  sym->name);
Packit Service ac8aad
			continue;
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		/* add rela in .rela__mcount_loc to fill in function pointer */
Packit Service ac8aad
		ALLOC_LINK(rela, &relasec->relas);
Packit Service ac8aad
		rela->sym = sym;
Packit Service ac8aad
		rela->type = R_X86_64_64;
Packit Service ac8aad
		rela->addend = 0;
Packit Service ac8aad
		rela->offset = index * sizeof(*funcs);
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Modify the first instruction of the function to "callq
Packit Service ac8aad
		 * __fentry__" so that ftrace will be happy.
Packit Service ac8aad
		 */
Packit Service ac8aad
		newdata = malloc(sym->sec->data->d_size);
Packit Service ac8aad
		memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size);
Packit Service ac8aad
		sym->sec->data->d_buf = newdata;
Packit Service ac8aad
		insn = newdata;
Packit Service ac8aad
		if (insn[0] != 0xf)
Packit Service ac8aad
			ERROR("%s: unexpected instruction at the start of the function",
Packit Service ac8aad
			      sym->name);
Packit Service ac8aad
		insn[0] = 0xe8;
Packit Service ac8aad
		insn[1] = 0;
Packit Service ac8aad
		insn[2] = 0;
Packit Service ac8aad
		insn[3] = 0;
Packit Service ac8aad
		insn[4] = 0;
Packit Service ac8aad
Packit Service ac8aad
		rela = list_first_entry(&sym->sec->rela->relas, struct rela,
Packit Service ac8aad
					list);
Packit Service ac8aad
		rela->type = R_X86_64_PC32;
Packit Service ac8aad
Packit Service ac8aad
		index++;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* sanity check, index should equal nr */
Packit Service ac8aad
	if (index != nr)
Packit Service ac8aad
		ERROR("size mismatch in funcs sections");
Packit Service ac8aad
}
Packit Service ac8aad
#endif
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * This function strips out symbols that were referenced by changed rela
Packit Service ac8aad
 * sections, but the rela entries that referenced them were converted to
Packit Service ac8aad
 * dynrelas and are no longer needed.
Packit Service ac8aad
 */
Packit Service ac8aad
static void kpatch_strip_unneeded_syms(struct kpatch_elf *kelf,
Packit Service ac8aad
				       struct lookup_table *table)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym, *safe;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry_safe(sym, safe, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->strip) {
Packit Service ac8aad
			list_del(&sym->list);
Packit Service ac8aad
			free(sym);
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_create_strings_elements(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	/* create .kpatch.strings */
Packit Service ac8aad
Packit Service ac8aad
	/* allocate section resources */
Packit Service ac8aad
	ALLOC_LINK(sec, &kelf->sections);
Packit Service ac8aad
	sec->name = ".kpatch.strings";
Packit Service ac8aad
Packit Service ac8aad
	/* set data */
Packit Service ac8aad
	sec->data = malloc(sizeof(*sec->data));
Packit Service ac8aad
	if (!sec->data)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	sec->data->d_type = ELF_T_BYTE;
Packit Service ac8aad
Packit Service ac8aad
	/* set section header */
Packit Service ac8aad
	sec->sh.sh_type = SHT_PROGBITS;
Packit Service ac8aad
	sec->sh.sh_entsize = 1;
Packit Service ac8aad
	sec->sh.sh_addralign = 1;
Packit Service ac8aad
	sec->sh.sh_flags = SHF_ALLOC;
Packit Service ac8aad
Packit Service ac8aad
	/* create .kpatch.strings section symbol (reuse sym variable) */
Packit Service ac8aad
Packit Service ac8aad
	ALLOC_LINK(sym, &kelf->symbols);
Packit Service ac8aad
	sym->sec = sec;
Packit Service ac8aad
	sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
Packit Service ac8aad
	sym->type = STT_SECTION;
Packit Service ac8aad
	sym->bind = STB_LOCAL;
Packit Service ac8aad
	sym->name = ".kpatch.strings";
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void kpatch_build_strings_section_data(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct string *string;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	int size;
Packit Service ac8aad
	char *strtab;
Packit Service ac8aad
Packit Service ac8aad
	sec = find_section_by_name(&kelf->sections, ".kpatch.strings");
Packit Service ac8aad
	if (!sec)
Packit Service ac8aad
		ERROR("can't find .kpatch.strings");
Packit Service ac8aad
Packit Service ac8aad
	/* determine size */
Packit Service ac8aad
	size = 0;
Packit Service ac8aad
	list_for_each_entry(string, &kelf->strings, list)
Packit Service ac8aad
		size += strlen(string->name) + 1;
Packit Service ac8aad
Packit Service ac8aad
	/* allocate section resources */
Packit Service ac8aad
	strtab = malloc(size);
Packit Service ac8aad
	if (!strtab)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	sec->data->d_buf = strtab;
Packit Service ac8aad
	sec->data->d_size = size;
Packit Service ac8aad
Packit Service ac8aad
	/* populate strings section data */
Packit Service ac8aad
	list_for_each_entry(string, &kelf->strings, list) {
Packit Service ac8aad
		strcpy(strtab, string->name);
Packit Service ac8aad
		strtab += strlen(string->name) + 1;
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct arguments {
Packit Service ac8aad
	char *args[7];
Packit Service ac8aad
	int debug;
Packit Service ac8aad
};
Packit Service ac8aad
Packit Service ac8aad
static char args_doc[] = "original.o patched.o parent-name parent-symtab Module.symvers patch-module-name output.o";
Packit Service ac8aad
Packit Service ac8aad
static struct argp_option options[] = {
Packit Service ac8aad
	{"debug", 'd', NULL, 0, "Show debug output" },
Packit Service ac8aad
	{ NULL }
Packit Service ac8aad
};
Packit Service ac8aad
Packit Service ac8aad
static error_t parse_opt (int key, char *arg, struct argp_state *state)
Packit Service ac8aad
{
Packit Service ac8aad
	/* Get the input argument from argp_parse, which we
Packit Service ac8aad
	   know is a pointer to our arguments structure. */
Packit Service ac8aad
	struct arguments *arguments = state->input;
Packit Service ac8aad
Packit Service ac8aad
	switch (key)
Packit Service ac8aad
	{
Packit Service ac8aad
		case 'd':
Packit Service ac8aad
			arguments->debug = 1;
Packit Service ac8aad
			break;
Packit Service ac8aad
		case ARGP_KEY_ARG:
Packit Service ac8aad
			if (state->arg_num >= 7)
Packit Service ac8aad
				/* Too many arguments. */
Packit Service ac8aad
				argp_usage (state);
Packit Service ac8aad
			arguments->args[state->arg_num] = arg;
Packit Service ac8aad
			break;
Packit Service ac8aad
		case ARGP_KEY_END:
Packit Service ac8aad
			if (state->arg_num < 7)
Packit Service ac8aad
				/* Not enough arguments. */
Packit Service ac8aad
				argp_usage (state);
Packit Service ac8aad
			break;
Packit Service ac8aad
		default:
Packit Service ac8aad
			return ARGP_ERR_UNKNOWN;
Packit Service ac8aad
	}
Packit Service ac8aad
	return 0;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static struct argp argp = { options, parse_opt, args_doc, NULL };
Packit Service ac8aad
Packit Service ac8aad
int main(int argc, char *argv[])
Packit Service ac8aad
{
Packit Service ac8aad
	struct kpatch_elf *kelf_base, *kelf_patched, *kelf_out;
Packit Service ac8aad
	struct arguments arguments;
Packit Service ac8aad
	int num_changed, callbacks_exist, new_globals_exist;
Packit Service ac8aad
	struct lookup_table *lookup;
Packit Service ac8aad
	struct section *sec, *symtab;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	char *hint = NULL, *orig_obj, *patched_obj, *parent_name;
Packit Service ac8aad
	char *parent_symtab, *mod_symvers, *patch_name, *output_obj;
Packit Service ac8aad
	struct sym_compare_type *base_locals;
Packit Service ac8aad
Packit Service ac8aad
	arguments.debug = 0;
Packit Service ac8aad
	argp_parse (&argp, argc, argv, 0, NULL, &arguments);
Packit Service ac8aad
	if (arguments.debug)
Packit Service ac8aad
		loglevel = DEBUG;
Packit Service ac8aad
Packit Service ac8aad
	elf_version(EV_CURRENT);
Packit Service ac8aad
Packit Service ac8aad
	orig_obj      = arguments.args[0];
Packit Service ac8aad
	patched_obj   = arguments.args[1];
Packit Service ac8aad
	parent_name   = arguments.args[2];
Packit Service ac8aad
	parent_symtab = arguments.args[3];
Packit Service ac8aad
	mod_symvers   = arguments.args[4];
Packit Service ac8aad
	patch_name    = arguments.args[5];
Packit Service ac8aad
	output_obj    = arguments.args[6];
Packit Service ac8aad
Packit Service ac8aad
	childobj = basename(orig_obj);
Packit Service ac8aad
Packit Service ac8aad
	kelf_base = kpatch_elf_open(orig_obj);
Packit Service ac8aad
	kelf_patched = kpatch_elf_open(patched_obj);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_bundle_symbols(kelf_base);
Packit Service ac8aad
	kpatch_bundle_symbols(kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf);
Packit Service ac8aad
	kpatch_check_program_headers(kelf_base->elf);
Packit Service ac8aad
	kpatch_check_program_headers(kelf_patched->elf);
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, &kelf_base->symbols, list) {
Packit Service ac8aad
		if (sym->type == STT_FILE) {
Packit Service ac8aad
			hint = sym->name;
Packit Service ac8aad
			break;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
	if (!hint) {
Packit Service ac8aad
		log_normal("WARNING: FILE symbol not found in base. Stripped object file or assembly source?\n");
Packit Service ac8aad
		return EXIT_STATUS_NO_CHANGE;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* create symbol lookup table */
Packit Service ac8aad
	base_locals = kpatch_elf_locals(kelf_base);
Packit Service ac8aad
	lookup = lookup_open(parent_symtab, mod_symvers, hint, base_locals);
Packit Service ac8aad
	free(base_locals);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_mark_grouped_sections(kelf_patched);
Packit Service ac8aad
	kpatch_replace_sections_syms(kelf_base);
Packit Service ac8aad
	kpatch_replace_sections_syms(kelf_patched);
Packit Service ac8aad
	kpatch_rename_mangled_functions(kelf_base, kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_correlate_elfs(kelf_base, kelf_patched);
Packit Service ac8aad
	kpatch_correlate_static_local_variables(kelf_base, kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * After this point, we don't care about kelf_base anymore.
Packit Service ac8aad
	 * We access its sections via the twin pointers in the
Packit Service ac8aad
	 * section, symbol, and rela lists of kelf_patched.
Packit Service ac8aad
	 */
Packit Service ac8aad
	kpatch_mark_ignored_sections(kelf_patched);
Packit Service ac8aad
	kpatch_compare_correlated_elements(kelf_patched);
Packit Service ac8aad
	kpatch_check_func_profiling_calls(kelf_patched);
Packit Service ac8aad
	kpatch_elf_teardown(kelf_base);
Packit Service ac8aad
	kpatch_elf_free(kelf_base);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_mark_ignored_functions_same(kelf_patched);
Packit Service ac8aad
	kpatch_mark_ignored_sections_same(kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_include_standard_elements(kelf_patched);
Packit Service ac8aad
	num_changed = kpatch_include_changed_functions(kelf_patched);
Packit Service ac8aad
	kpatch_include_debug_sections(kelf_patched);
Packit Service ac8aad
	callbacks_exist = kpatch_include_callback_elements(kelf_patched);
Packit Service ac8aad
	kpatch_include_force_elements(kelf_patched);
Packit Service ac8aad
	new_globals_exist = kpatch_include_new_globals(kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_print_changes(kelf_patched);
Packit Service ac8aad
	kpatch_dump_kelf(kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_process_special_sections(kelf_patched);
Packit Service ac8aad
	kpatch_verify_patchability(kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	if (!num_changed && !new_globals_exist) {
Packit Service ac8aad
		if (callbacks_exist)
Packit Service ac8aad
			log_debug("no changed functions were found, but callbacks exist\n");
Packit Service ac8aad
		else {
Packit Service ac8aad
			log_debug("no changed functions were found\n");
Packit Service ac8aad
			return EXIT_STATUS_NO_CHANGE;
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* this is destructive to kelf_patched */
Packit Service ac8aad
	kpatch_migrate_included_elements(kelf_patched, &kelf_out);
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Teardown kelf_patched since we shouldn't access sections or symbols
Packit Service ac8aad
	 * through it anymore.  Don't free however, since our section and symbol
Packit Service ac8aad
	 * name fields still point to strings in the Elf object owned by
Packit Service ac8aad
	 * kpatch_patched.
Packit Service ac8aad
	 */
Packit Service ac8aad
	kpatch_elf_teardown(kelf_patched);
Packit Service ac8aad
Packit Service ac8aad
	/* create strings, patches, and dynrelas sections */
Packit Service ac8aad
	kpatch_create_strings_elements(kelf_out);
Packit Service ac8aad
	kpatch_create_patches_sections(kelf_out, lookup, parent_name);
Packit Service ac8aad
	kpatch_create_intermediate_sections(kelf_out, lookup, parent_name, patch_name);
Packit Service ac8aad
	kpatch_create_kpatch_arch_section(kelf_out, parent_name);
Packit Service ac8aad
	kpatch_create_callbacks_objname_rela(kelf_out, parent_name);
Packit Service ac8aad
	kpatch_build_strings_section_data(kelf_out);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_create_mcount_sections(kelf_out);
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 *  At this point, the set of output sections and symbols is
Packit Service ac8aad
	 *  finalized.  Reorder the symbols into linker-compliant
Packit Service ac8aad
	 *  order and index all the symbols and sections.  After the
Packit Service ac8aad
	 *  indexes have been established, update index data
Packit Service ac8aad
	 *  throughout the structure.
Packit Service ac8aad
	 */
Packit Service ac8aad
	kpatch_reorder_symbols(kelf_out);
Packit Service ac8aad
	kpatch_strip_unneeded_syms(kelf_out, lookup);
Packit Service ac8aad
	kpatch_reindex_elements(kelf_out);
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Update rela section headers and rebuild the rela section data
Packit Service ac8aad
	 * buffers from the relas lists.
Packit Service ac8aad
	 */
Packit Service ac8aad
	symtab = find_section_by_name(&kelf_out->sections, ".symtab");
Packit Service ac8aad
	list_for_each_entry(sec, &kelf_out->sections, list) {
Packit Service ac8aad
		if (!is_rela_section(sec))
Packit Service ac8aad
			continue;
Packit Service ac8aad
		sec->sh.sh_link = symtab->index;
Packit Service ac8aad
		sec->sh.sh_info = sec->base->index;
Packit Service ac8aad
		kpatch_rebuild_rela_section_data(sec);
Packit Service ac8aad
	}
Packit Service ac8aad
	kpatch_check_relocations(kelf_out);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_create_shstrtab(kelf_out);
Packit Service ac8aad
	kpatch_create_strtab(kelf_out);
Packit Service ac8aad
	kpatch_create_symtab(kelf_out);
Packit Service ac8aad
	kpatch_dump_kelf(kelf_out);
Packit Service ac8aad
	kpatch_write_output_elf(kelf_out, kelf_patched->elf, output_obj);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_elf_free(kelf_patched);
Packit Service ac8aad
	kpatch_elf_teardown(kelf_out);
Packit Service ac8aad
	kpatch_elf_free(kelf_out);
Packit Service ac8aad
Packit Service ac8aad
	return EXIT_STATUS_SUCCESS;
Packit Service ac8aad
}