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

Packit c71e3f
/*
Packit c71e3f
 * create-kpatch-module.c
Packit c71e3f
 *
Packit c71e3f
 * This program is free software; you can redistribute it and/or
Packit c71e3f
 * modify it under the terms of the GNU General Public License
Packit c71e3f
 * as published by the Free Software Foundation; either version 2
Packit c71e3f
 * of the License, or (at your option) any later version.
Packit c71e3f
 *
Packit c71e3f
 * This program is distributed in the hope that it will be useful,
Packit c71e3f
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit c71e3f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit c71e3f
 * GNU General Public License for more details.
Packit c71e3f
 *
Packit c71e3f
 * You should have received a copy of the GNU General Public License
Packit c71e3f
 * along with this program; if not, write to the Free Software
Packit c71e3f
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
Packit c71e3f
 * 02110-1301, USA.
Packit c71e3f
 */
Packit c71e3f
Packit c71e3f
#include <string.h>
Packit c71e3f
#include <stdlib.h>
Packit c71e3f
#include <libgen.h>
Packit c71e3f
#include <argp.h>
Packit c71e3f
Packit c71e3f
#include "log.h"
Packit c71e3f
#include "kpatch-elf.h"
Packit c71e3f
#include "kpatch-intermediate.h"
Packit c71e3f
#include "kpatch-patch.h"
Packit c71e3f
Packit c71e3f
/* For log.h */
Packit c71e3f
char *childobj;
Packit c71e3f
enum loglevel loglevel = NORMAL;
Packit c71e3f
Packit c71e3f
/*
Packit c71e3f
 * Create .kpatch.dynrelas from .kpatch.relocations and .kpatch.symbols sections
Packit c71e3f
 *
Packit c71e3f
 * Iterate through .kpatch.relocations and fill in the corresponding dynrela
Packit c71e3f
 * entry using information from .kpatch.relocations and .kpatch.symbols
Packit c71e3f
 */
Packit c71e3f
static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section *krelasec,
Packit c71e3f
					 struct section *ksymsec, struct section *strsec)
Packit c71e3f
{
Packit c71e3f
	struct kpatch_patch_dynrela *dynrelas;
Packit c71e3f
	struct kpatch_relocation *krelas;
Packit c71e3f
	struct kpatch_symbol *ksym, *ksyms;
Packit c71e3f
	struct section *dynsec;
Packit c71e3f
	struct symbol *sym;
Packit c71e3f
	struct rela *rela;
Packit c71e3f
	int index, nr, offset, dest_offset, objname_offset, name_offset;
Packit c71e3f
Packit c71e3f
	ksyms = ksymsec->data->d_buf;
Packit c71e3f
	krelas = krelasec->data->d_buf;
Packit c71e3f
	nr = krelasec->data->d_size / sizeof(*krelas);
Packit c71e3f
Packit c71e3f
	dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr);
Packit c71e3f
	dynrelas = dynsec->data->d_buf;
Packit c71e3f
Packit c71e3f
	for (index = 0; index < nr; index++) {
Packit c71e3f
		offset = index * sizeof(*krelas);
Packit c71e3f
Packit c71e3f
		/*
Packit c71e3f
		 * To fill in each dynrela entry, find dest location,
Packit c71e3f
		 * objname offset, ksym, and symbol name offset
Packit c71e3f
		 */
Packit c71e3f
Packit c71e3f
		/* Get dest location */
Packit c71e3f
		rela = find_rela_by_offset(krelasec->rela,
Packit c71e3f
					   offset + offsetof(struct kpatch_relocation, dest));
Packit c71e3f
		if (!rela)
Packit c71e3f
			ERROR("find_rela_by_offset");
Packit c71e3f
		sym = rela->sym;
Packit c71e3f
		dest_offset = rela->addend;
Packit c71e3f
Packit c71e3f
		/* Get objname offset */
Packit c71e3f
		rela = find_rela_by_offset(krelasec->rela,
Packit c71e3f
					   offset + offsetof(struct kpatch_relocation, objname));
Packit c71e3f
		if (!rela)
Packit c71e3f
			ERROR("find_rela_by_offset");
Packit c71e3f
		objname_offset = rela->addend;
Packit c71e3f
Packit c71e3f
		/* Get ksym (.kpatch.symbols entry) and symbol name offset */
Packit c71e3f
		rela = find_rela_by_offset(krelasec->rela,
Packit c71e3f
					   offset + offsetof(struct kpatch_relocation, ksym));
Packit c71e3f
		if (!rela)
Packit c71e3f
			ERROR("find_rela_by_offset");
Packit c71e3f
		ksym = ksyms + (rela->addend / sizeof(*ksyms));
Packit c71e3f
Packit c71e3f
		offset = index * sizeof(*ksyms);
Packit c71e3f
		rela = find_rela_by_offset(ksymsec->rela,
Packit c71e3f
					   offset + offsetof(struct kpatch_symbol, name));
Packit c71e3f
		if (!rela)
Packit c71e3f
			ERROR("find_rela_by_offset");
Packit c71e3f
		name_offset = rela->addend;
Packit c71e3f
Packit c71e3f
		/* Fill in dynrela entry */
Packit c71e3f
		dynrelas[index].src = ksym->src;
Packit c71e3f
		dynrelas[index].addend = krelas[index].addend;
Packit c71e3f
		dynrelas[index].type = krelas[index].type;
Packit c71e3f
		dynrelas[index].external = krelas[index].external;
Packit c71e3f
		dynrelas[index].sympos = ksym->pos;
Packit c71e3f
Packit c71e3f
		/* dest */
Packit c71e3f
		ALLOC_LINK(rela, &dynsec->rela->relas);
Packit c71e3f
		rela->sym = sym;
Packit c71e3f
		rela->type = R_X86_64_64;
Packit c71e3f
		rela->addend = dest_offset;
Packit c71e3f
		rela->offset = index * sizeof(*dynrelas);
Packit c71e3f
Packit c71e3f
		/* name */
Packit c71e3f
		ALLOC_LINK(rela, &dynsec->rela->relas);
Packit c71e3f
		rela->sym = strsec->secsym;
Packit c71e3f
		rela->type = R_X86_64_64;
Packit c71e3f
		rela->addend = name_offset;
Packit c71e3f
		rela->offset = index * sizeof(*dynrelas) + \
Packit c71e3f
			       offsetof(struct kpatch_patch_dynrela, name);
Packit c71e3f
Packit c71e3f
		/* objname */
Packit c71e3f
		ALLOC_LINK(rela, &dynsec->rela->relas);
Packit c71e3f
		rela->sym = strsec->secsym;
Packit c71e3f
		rela->type = R_X86_64_64;
Packit c71e3f
		rela->addend = objname_offset;
Packit c71e3f
		rela->offset = index * sizeof(*dynrelas) + \
Packit c71e3f
			       offsetof(struct kpatch_patch_dynrela, objname);
Packit c71e3f
	}
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
static void remove_intermediate_sections(struct kpatch_elf *kelf)
Packit c71e3f
{
Packit c71e3f
	size_t i;
Packit c71e3f
	char *intermediate_sections[] = {
Packit c71e3f
		".kpatch.symbols",
Packit c71e3f
		".rela.kpatch.symbols",
Packit c71e3f
		".kpatch.relocations",
Packit c71e3f
		".rela.kpatch.relocations",
Packit c71e3f
		".kpatch.arch",
Packit c71e3f
		".rela.kpatch.arch"
Packit c71e3f
	};
Packit c71e3f
Packit c71e3f
	for (i = 0; i < sizeof(intermediate_sections)/sizeof(intermediate_sections[0]); i++)
Packit c71e3f
		kpatch_remove_and_free_section(kelf, intermediate_sections[i]);
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
struct arguments {
Packit c71e3f
	char *args[2];
Packit c71e3f
	int debug;
Packit c71e3f
};
Packit c71e3f
Packit c71e3f
static char args_doc[] = "input.o output.o";
Packit c71e3f
Packit c71e3f
static struct argp_option options[] = {
Packit c71e3f
	{"debug", 'd', 0, 0, "Show debug output" },
Packit c71e3f
	{ 0 }
Packit c71e3f
};
Packit c71e3f
Packit c71e3f
static error_t parse_opt (int key, char *arg, struct argp_state *state)
Packit c71e3f
{
Packit c71e3f
	/* Get the input argument from argp_parse, which we
Packit c71e3f
	   know is a pointer to our arguments structure. */
Packit c71e3f
	struct arguments *arguments = state->input;
Packit c71e3f
Packit c71e3f
	switch (key)
Packit c71e3f
	{
Packit c71e3f
		case 'd':
Packit c71e3f
			arguments->debug = 1;
Packit c71e3f
			break;
Packit c71e3f
		case ARGP_KEY_ARG:
Packit c71e3f
			if (state->arg_num >= 2)
Packit c71e3f
				/* Too many arguments. */
Packit c71e3f
				argp_usage (state);
Packit c71e3f
			arguments->args[state->arg_num] = arg;
Packit c71e3f
			break;
Packit c71e3f
		case ARGP_KEY_END:
Packit c71e3f
			if (state->arg_num < 2)
Packit c71e3f
				/* Not enough arguments. */
Packit c71e3f
				argp_usage (state);
Packit c71e3f
			break;
Packit c71e3f
		default:
Packit c71e3f
			return ARGP_ERR_UNKNOWN;
Packit c71e3f
	}
Packit c71e3f
	return 0;
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
static struct argp argp = { options, parse_opt, args_doc, 0 };
Packit c71e3f
Packit c71e3f
int main(int argc, char *argv[])
Packit c71e3f
{
Packit c71e3f
	struct kpatch_elf *kelf;
Packit c71e3f
	struct section *symtab, *sec;
Packit c71e3f
	struct section *ksymsec, *krelasec, *strsec;
Packit c71e3f
	struct arguments arguments;
Packit c71e3f
	int ksyms_nr, krelas_nr;
Packit c71e3f
Packit c71e3f
	arguments.debug = 0;
Packit c71e3f
	argp_parse (&argp, argc, argv, 0, 0, &arguments);
Packit c71e3f
	if (arguments.debug)
Packit c71e3f
		loglevel = DEBUG;
Packit c71e3f
Packit c71e3f
	elf_version(EV_CURRENT);
Packit c71e3f
Packit c71e3f
	childobj = basename(arguments.args[0]);
Packit c71e3f
Packit c71e3f
	kelf = kpatch_elf_open(arguments.args[0]);
Packit c71e3f
Packit c71e3f
	/*
Packit c71e3f
	 * Sanity checks:
Packit c71e3f
	 * - Make sure all the required sections exist
Packit c71e3f
	 * - Make sure that the number of entries in
Packit c71e3f
	 *   .kpatch.{symbols,relocations} match
Packit c71e3f
	 */
Packit c71e3f
	strsec = find_section_by_name(&kelf->sections, ".kpatch.strings");
Packit c71e3f
	if (!strsec)
Packit c71e3f
		ERROR("missing .kpatch.strings");
Packit c71e3f
Packit c71e3f
	ksymsec = find_section_by_name(&kelf->sections, ".kpatch.symbols");
Packit c71e3f
	if (!ksymsec)
Packit c71e3f
		ERROR("missing .kpatch.symbols section");
Packit c71e3f
	ksyms_nr = ksymsec->data->d_size / sizeof(struct kpatch_symbol);
Packit c71e3f
Packit c71e3f
	krelasec = find_section_by_name(&kelf->sections, ".kpatch.relocations");
Packit c71e3f
	if (!krelasec)
Packit c71e3f
		ERROR("missing .kpatch.relocations section");
Packit c71e3f
	krelas_nr = krelasec->data->d_size / sizeof(struct kpatch_relocation);
Packit c71e3f
Packit c71e3f
	if (krelas_nr != ksyms_nr)
Packit c71e3f
		ERROR("number of krelas and ksyms do not match");
Packit c71e3f
Packit c71e3f
	/* Create dynrelas from .kpatch.{relocations,symbols} sections */
Packit c71e3f
	create_dynamic_rela_sections(kelf, krelasec, ksymsec, strsec);
Packit c71e3f
	remove_intermediate_sections(kelf);
Packit c71e3f
Packit c71e3f
	kpatch_reindex_elements(kelf);
Packit c71e3f
Packit c71e3f
	symtab = find_section_by_name(&kelf->sections, ".symtab");
Packit c71e3f
	list_for_each_entry(sec, &kelf->sections, list) {
Packit c71e3f
		if (!is_rela_section(sec))
Packit c71e3f
			continue;
Packit c71e3f
		sec->sh.sh_link = symtab->index;
Packit c71e3f
		sec->sh.sh_info = sec->base->index;
Packit c71e3f
		kpatch_rebuild_rela_section_data(sec);
Packit c71e3f
	}
Packit c71e3f
Packit c71e3f
	kpatch_create_shstrtab(kelf);
Packit c71e3f
	kpatch_create_strtab(kelf);
Packit c71e3f
	kpatch_create_symtab(kelf);
Packit c71e3f
Packit c71e3f
	kpatch_write_output_elf(kelf, kelf->elf, arguments.args[1]);
Packit c71e3f
	kpatch_elf_teardown(kelf);
Packit c71e3f
	kpatch_elf_free(kelf);
Packit c71e3f
Packit c71e3f
	return 0;
Packit c71e3f
}