Blame kpatch-build/create-kpatch-module.c

Packit Service ac8aad
/*
Packit Service ac8aad
 * create-kpatch-module.c
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
#include <string.h>
Packit Service ac8aad
#include <stdlib.h>
Packit Service ac8aad
#include <libgen.h>
Packit Service ac8aad
#include <argp.h>
Packit Service ac8aad
Packit Service ac8aad
#include "log.h"
Packit Service ac8aad
#include "kpatch-elf.h"
Packit Service ac8aad
#include "kpatch-intermediate.h"
Packit Service ac8aad
#include "kpatch-patch.h"
Packit Service ac8aad
Packit Service ac8aad
/* For log.h */
Packit Service ac8aad
char *childobj;
Packit Service ac8aad
enum loglevel loglevel = NORMAL;
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * Create .kpatch.dynrelas from .kpatch.relocations and .kpatch.symbols sections
Packit Service ac8aad
 *
Packit Service ac8aad
 * Iterate through .kpatch.relocations and fill in the corresponding dynrela
Packit Service ac8aad
 * entry using information from .kpatch.relocations and .kpatch.symbols
Packit Service ac8aad
 */
Packit Service ac8aad
static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section *krelasec,
Packit Service ac8aad
					 struct section *ksymsec, struct section *strsec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct kpatch_patch_dynrela *dynrelas;
Packit Service ac8aad
	struct kpatch_relocation *krelas;
Packit Service ac8aad
	struct kpatch_symbol *ksym, *ksyms;
Packit Service ac8aad
	struct section *dynsec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int index, nr, offset, dest_offset, objname_offset, name_offset;
Packit Service ac8aad
Packit Service ac8aad
	ksyms = ksymsec->data->d_buf;
Packit Service ac8aad
	krelas = krelasec->data->d_buf;
Packit Service ac8aad
	nr = krelasec->data->d_size / sizeof(*krelas);
Packit Service ac8aad
Packit Service ac8aad
	dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr);
Packit Service ac8aad
	dynrelas = dynsec->data->d_buf;
Packit Service ac8aad
Packit Service ac8aad
	for (index = 0; index < nr; index++) {
Packit Service ac8aad
		offset = index * sizeof(*krelas);
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * To fill in each dynrela entry, find dest location,
Packit Service ac8aad
		 * objname offset, ksym, and symbol name offset
Packit Service ac8aad
		 */
Packit Service ac8aad
Packit Service ac8aad
		/* Get dest location */
Packit Service ac8aad
		rela = find_rela_by_offset(krelasec->rela,
Packit Service ac8aad
					   offset + offsetof(struct kpatch_relocation, dest));
Packit Service ac8aad
		if (!rela)
Packit Service ac8aad
			ERROR("find_rela_by_offset");
Packit Service ac8aad
		sym = rela->sym;
Packit Service ac8aad
		dest_offset = rela->addend;
Packit Service ac8aad
Packit Service ac8aad
		/* Get objname offset */
Packit Service ac8aad
		rela = find_rela_by_offset(krelasec->rela,
Packit Service ac8aad
					   offset + offsetof(struct kpatch_relocation, objname));
Packit Service ac8aad
		if (!rela)
Packit Service ac8aad
			ERROR("find_rela_by_offset");
Packit Service ac8aad
		objname_offset = rela->addend;
Packit Service ac8aad
Packit Service ac8aad
		/* Get ksym (.kpatch.symbols entry) and symbol name offset */
Packit Service ac8aad
		rela = find_rela_by_offset(krelasec->rela,
Packit Service ac8aad
					   offset + offsetof(struct kpatch_relocation, ksym));
Packit Service ac8aad
		if (!rela)
Packit Service ac8aad
			ERROR("find_rela_by_offset");
Packit Service ac8aad
		ksym = ksyms + (rela->addend / sizeof(*ksyms));
Packit Service ac8aad
Packit Service ac8aad
		offset = index * sizeof(*ksyms);
Packit Service ac8aad
		rela = find_rela_by_offset(ksymsec->rela,
Packit Service ac8aad
					   offset + offsetof(struct kpatch_symbol, name));
Packit Service ac8aad
		if (!rela)
Packit Service ac8aad
			ERROR("find_rela_by_offset");
Packit Service ac8aad
		name_offset = rela->addend;
Packit Service ac8aad
Packit Service ac8aad
		/* Fill in dynrela entry */
Packit Service ac8aad
		dynrelas[index].src = ksym->src;
Packit Service ac8aad
		dynrelas[index].addend = krelas[index].addend;
Packit Service ac8aad
		dynrelas[index].type = krelas[index].type;
Packit Service ac8aad
		dynrelas[index].external = krelas[index].external;
Packit Service ac8aad
		dynrelas[index].sympos = ksym->pos;
Packit Service ac8aad
Packit Service ac8aad
		/* dest */
Packit Service ac8aad
		ALLOC_LINK(rela, &dynsec->rela->relas);
Packit Service ac8aad
		rela->sym = sym;
Packit Service ac8aad
		rela->type = R_X86_64_64;
Packit Service ac8aad
		rela->addend = dest_offset;
Packit Service ac8aad
		rela->offset = index * sizeof(*dynrelas);
Packit Service ac8aad
Packit Service ac8aad
		/* name */
Packit Service ac8aad
		ALLOC_LINK(rela, &dynsec->rela->relas);
Packit Service ac8aad
		rela->sym = strsec->secsym;
Packit Service ac8aad
		rela->type = R_X86_64_64;
Packit Service ac8aad
		rela->addend = name_offset;
Packit Service ac8aad
		rela->offset = index * sizeof(*dynrelas) + \
Packit Service ac8aad
			       offsetof(struct kpatch_patch_dynrela, name);
Packit Service ac8aad
Packit Service ac8aad
		/* objname */
Packit Service ac8aad
		ALLOC_LINK(rela, &dynsec->rela->relas);
Packit Service ac8aad
		rela->sym = strsec->secsym;
Packit Service ac8aad
		rela->type = R_X86_64_64;
Packit Service ac8aad
		rela->addend = objname_offset;
Packit Service ac8aad
		rela->offset = index * sizeof(*dynrelas) + \
Packit Service ac8aad
			       offsetof(struct kpatch_patch_dynrela, objname);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
static void remove_intermediate_sections(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	size_t i;
Packit Service ac8aad
	char *intermediate_sections[] = {
Packit Service ac8aad
		".kpatch.symbols",
Packit Service ac8aad
		".rela.kpatch.symbols",
Packit Service ac8aad
		".kpatch.relocations",
Packit Service ac8aad
		".rela.kpatch.relocations",
Packit Service ac8aad
		".kpatch.arch",
Packit Service ac8aad
		".rela.kpatch.arch"
Packit Service ac8aad
	};
Packit Service ac8aad
Packit Service ac8aad
	for (i = 0; i < sizeof(intermediate_sections)/sizeof(intermediate_sections[0]); i++)
Packit Service ac8aad
		kpatch_remove_and_free_section(kelf, intermediate_sections[i]);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct arguments {
Packit Service ac8aad
	char *args[2];
Packit Service ac8aad
	int debug;
Packit Service ac8aad
};
Packit Service ac8aad
Packit Service ac8aad
static char args_doc[] = "input.o output.o";
Packit Service ac8aad
Packit Service ac8aad
static struct argp_option options[] = {
Packit Service ac8aad
	{"debug", 'd', 0, 0, "Show debug output" },
Packit Service ac8aad
	{ 0 }
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 >= 2)
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 < 2)
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, 0 };
Packit Service ac8aad
Packit Service ac8aad
int main(int argc, char *argv[])
Packit Service ac8aad
{
Packit Service ac8aad
	struct kpatch_elf *kelf;
Packit Service ac8aad
	struct section *symtab, *sec;
Packit Service ac8aad
	struct section *ksymsec, *krelasec, *strsec;
Packit Service ac8aad
	struct arguments arguments;
Packit Service ac8aad
	int ksyms_nr, krelas_nr;
Packit Service ac8aad
Packit Service ac8aad
	arguments.debug = 0;
Packit Service ac8aad
	argp_parse (&argp, argc, argv, 0, 0, &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
	childobj = basename(arguments.args[0]);
Packit Service ac8aad
Packit Service ac8aad
	kelf = kpatch_elf_open(arguments.args[0]);
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * Sanity checks:
Packit Service ac8aad
	 * - Make sure all the required sections exist
Packit Service ac8aad
	 * - Make sure that the number of entries in
Packit Service ac8aad
	 *   .kpatch.{symbols,relocations} match
Packit Service ac8aad
	 */
Packit Service ac8aad
	strsec = find_section_by_name(&kelf->sections, ".kpatch.strings");
Packit Service ac8aad
	if (!strsec)
Packit Service ac8aad
		ERROR("missing .kpatch.strings");
Packit Service ac8aad
Packit Service ac8aad
	ksymsec = find_section_by_name(&kelf->sections, ".kpatch.symbols");
Packit Service ac8aad
	if (!ksymsec)
Packit Service ac8aad
		ERROR("missing .kpatch.symbols section");
Packit Service ac8aad
	ksyms_nr = ksymsec->data->d_size / sizeof(struct kpatch_symbol);
Packit Service ac8aad
Packit Service ac8aad
	krelasec = find_section_by_name(&kelf->sections, ".kpatch.relocations");
Packit Service ac8aad
	if (!krelasec)
Packit Service ac8aad
		ERROR("missing .kpatch.relocations section");
Packit Service ac8aad
	krelas_nr = krelasec->data->d_size / sizeof(struct kpatch_relocation);
Packit Service ac8aad
Packit Service ac8aad
	if (krelas_nr != ksyms_nr)
Packit Service ac8aad
		ERROR("number of krelas and ksyms do not match");
Packit Service ac8aad
Packit Service ac8aad
	/* Create dynrelas from .kpatch.{relocations,symbols} sections */
Packit Service ac8aad
	create_dynamic_rela_sections(kelf, krelasec, ksymsec, strsec);
Packit Service ac8aad
	remove_intermediate_sections(kelf);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_reindex_elements(kelf);
Packit Service ac8aad
Packit Service ac8aad
	symtab = find_section_by_name(&kelf->sections, ".symtab");
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
		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
Packit Service ac8aad
	kpatch_create_shstrtab(kelf);
Packit Service ac8aad
	kpatch_create_strtab(kelf);
Packit Service ac8aad
	kpatch_create_symtab(kelf);
Packit Service ac8aad
Packit Service ac8aad
	kpatch_write_output_elf(kelf, kelf->elf, arguments.args[1]);
Packit Service ac8aad
	kpatch_elf_teardown(kelf);
Packit Service ac8aad
	kpatch_elf_free(kelf);
Packit Service ac8aad
Packit Service ac8aad
	return 0;
Packit Service ac8aad
}