Blame kpatch-build/kpatch-elf.c

Packit Service ac8aad
/*
Packit Service ac8aad
 * kpatch-elf.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
/*
Packit Service ac8aad
 * This file provides a common api to create, inspect, and manipulate
Packit Service ac8aad
 * kpatch_elf objects.
Packit Service ac8aad
 */
Packit Service ac8aad
Packit Service ac8aad
#include <stdio.h>
Packit Service ac8aad
#include <stdlib.h>
Packit Service ac8aad
#include <string.h>
Packit Service ac8aad
#include <fcntl.h>
Packit Service ac8aad
#include <unistd.h>
Packit Service ac8aad
#include <error.h>
Packit Service ac8aad
#include <sys/types.h>
Packit Service ac8aad
#include <sys/stat.h>
Packit Service ac8aad
Packit Service ac8aad
#include "kpatch-elf.h"
Packit Service ac8aad
Packit Service ac8aad
/*******************
Packit Service ac8aad
 * Helper functions
Packit Service ac8aad
 ******************/
Packit Service ac8aad
Packit Service ac8aad
char *status_str(enum status status)
Packit Service ac8aad
{
Packit Service ac8aad
	switch(status) {
Packit Service ac8aad
	case NEW:
Packit Service ac8aad
		return "NEW";
Packit Service ac8aad
	case CHANGED:
Packit Service ac8aad
		return "CHANGED";
Packit Service ac8aad
	case SAME:
Packit Service ac8aad
		return "SAME";
Packit Service ac8aad
	default:
Packit Service ac8aad
		ERROR("status_str");
Packit Service ac8aad
	}
Packit Service ac8aad
	/* never reached */
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_rela_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	return (sec->sh.sh_type == SHT_RELA);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_text_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	return (sec->sh.sh_type == SHT_PROGBITS &&
Packit Service ac8aad
		(sec->sh.sh_flags & SHF_EXECINSTR));
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_debug_section(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	char *name;
Packit Service ac8aad
	if (is_rela_section(sec))
Packit Service ac8aad
		name = sec->base->name;
Packit Service ac8aad
	else
Packit Service ac8aad
		name = sec->name;
Packit Service ac8aad
Packit Service ac8aad
	return !strncmp(name, ".debug_", 7) ||
Packit Service ac8aad
	       !strncmp(name, ".eh_frame", 9);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct section *find_section_by_index(struct list_head *list, unsigned int index)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, list, list)
Packit Service ac8aad
		if (sec->index == index)
Packit Service ac8aad
			return sec;
Packit Service ac8aad
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct section *find_section_by_name(struct list_head *list, const char *name)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sec, list, list)
Packit Service ac8aad
		if (!strcmp(sec->name, name))
Packit Service ac8aad
			return sec;
Packit Service ac8aad
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct symbol *find_symbol_by_index(struct list_head *list, size_t index)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, list, list)
Packit Service ac8aad
		if (sym->index == index)
Packit Service ac8aad
			return sym;
Packit Service ac8aad
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct symbol *find_symbol_by_name(struct list_head *list, const char *name)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(sym, list, list)
Packit Service ac8aad
		if (sym->name && !strcmp(sym->name, name))
Packit Service ac8aad
			return sym;
Packit Service ac8aad
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct rela *find_rela_by_offset(struct section *relasec, unsigned int offset)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(rela, &relasec->relas, list) {
Packit Service ac8aad
		if (rela->offset == offset)
Packit Service ac8aad
			return rela;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	return NULL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/* returns the offset of the string in the string table */
Packit Service ac8aad
int offset_of_string(struct list_head *list, char *name)
Packit Service ac8aad
{
Packit Service ac8aad
	struct string *string;
Packit Service ac8aad
	int index = 0;
Packit Service ac8aad
Packit Service ac8aad
	/* try to find string in the string list */
Packit Service ac8aad
	list_for_each_entry(string, list, list) {
Packit Service ac8aad
		if (!strcmp(string->name, name))
Packit Service ac8aad
			return index;
Packit Service ac8aad
		index += strlen(string->name) + 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* allocate a new string */
Packit Service ac8aad
	ALLOC_LINK(string, list);
Packit Service ac8aad
	string->name = name;
Packit Service ac8aad
	return index;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	int rela_nr, index = 0, skip = 0;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	unsigned int symndx;
Packit Service ac8aad
Packit Service ac8aad
	/* find matching base (text/data) section */
Packit Service ac8aad
	sec->base = find_section_by_index(&kelf->sections, sec->sh.sh_info);
Packit Service ac8aad
	if (!sec->base)
Packit Service ac8aad
		ERROR("can't find base section for rela section %s", sec->name);
Packit Service ac8aad
Packit Service ac8aad
	/* create reverse link from base section to this rela section */
Packit Service ac8aad
	sec->base->rela = sec;
Packit Service ac8aad
Packit Service ac8aad
	rela_nr = sec->sh.sh_size / sec->sh.sh_entsize;
Packit Service ac8aad
Packit Service ac8aad
	log_debug("\n=== rela list for %s (%d entries) ===\n",
Packit Service ac8aad
		sec->base->name, rela_nr);
Packit Service ac8aad
Packit Service ac8aad
	if (is_debug_section(sec)) {
Packit Service ac8aad
		log_debug("skipping rela listing for .debug_* section\n");
Packit Service ac8aad
		skip = 1;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* read and store the rela entries */
Packit Service ac8aad
	while (rela_nr--) {
Packit Service ac8aad
		ALLOC_LINK(rela, &sec->relas);
Packit Service ac8aad
Packit Service ac8aad
		if (!gelf_getrela(sec->data, index, &rela->rela))
Packit Service ac8aad
			ERROR("gelf_getrela");
Packit Service ac8aad
		index++;
Packit Service ac8aad
Packit Service ac8aad
		rela->type = GELF_R_TYPE(rela->rela.r_info);
Packit Service ac8aad
		rela->addend = rela->rela.r_addend;
Packit Service ac8aad
		rela->offset = rela->rela.r_offset;
Packit Service ac8aad
		symndx = GELF_R_SYM(rela->rela.r_info);
Packit Service ac8aad
		rela->sym = find_symbol_by_index(&kelf->symbols, symndx);
Packit Service ac8aad
		if (!rela->sym)
Packit Service ac8aad
			ERROR("could not find rela entry symbol\n");
Packit Service ac8aad
		if (rela->sym->sec &&
Packit Service ac8aad
		    (rela->sym->sec->sh.sh_flags & SHF_STRINGS)) {
Packit Service ac8aad
			rela->string = rela->sym->sec->data->d_buf + rela->addend;
Packit Service ac8aad
			if (!rela->string)
Packit Service ac8aad
				ERROR("could not lookup rela string for %s+%d",
Packit Service ac8aad
				      rela->sym->name, rela->addend);
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		if (skip)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		log_debug("offset %d, type %d, %s %s %d", rela->offset,
Packit Service ac8aad
			rela->type, rela->sym->name,
Packit Service ac8aad
			(rela->addend < 0)?"-":"+", abs(rela->addend));
Packit Service ac8aad
		if (rela->string)
Packit Service ac8aad
			log_debug(" (string = %s)", rela->string);
Packit Service ac8aad
		log_debug("\n");
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_create_section_list(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	Elf_Scn *scn = NULL;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	size_t shstrndx, sections_nr;
Packit Service ac8aad
Packit Service ac8aad
	if (elf_getshdrnum(kelf->elf, &sections_nr))
Packit Service ac8aad
		ERROR("elf_getshdrnum");
Packit Service ac8aad
Packit Service ac8aad
	/*
Packit Service ac8aad
	 * elf_getshdrnum() includes section index 0 but elf_nextscn
Packit Service ac8aad
	 * doesn't return that section so subtract one.
Packit Service ac8aad
	 */
Packit Service ac8aad
	sections_nr--;
Packit Service ac8aad
Packit Service ac8aad
	if (elf_getshdrstrndx(kelf->elf, &shstrndx))
Packit Service ac8aad
		ERROR("elf_getshdrstrndx");
Packit Service ac8aad
Packit Service ac8aad
	log_debug("=== section list (%zu) ===\n", sections_nr);
Packit Service ac8aad
Packit Service ac8aad
	while (sections_nr--) {
Packit Service ac8aad
		ALLOC_LINK(sec, &kelf->sections);
Packit Service ac8aad
Packit Service ac8aad
		scn = elf_nextscn(kelf->elf, scn);
Packit Service ac8aad
		if (!scn)
Packit Service ac8aad
			ERROR("scn NULL");
Packit Service ac8aad
Packit Service ac8aad
		if (!gelf_getshdr(scn, &sec->sh))
Packit Service ac8aad
			ERROR("gelf_getshdr");
Packit Service ac8aad
Packit Service ac8aad
		sec->name = elf_strptr(kelf->elf, shstrndx, sec->sh.sh_name);
Packit Service ac8aad
		if (!sec->name)
Packit Service ac8aad
			ERROR("elf_strptr");
Packit Service ac8aad
Packit Service ac8aad
		sec->data = elf_getdata(scn, NULL);
Packit Service ac8aad
		if (!sec->data)
Packit Service ac8aad
			ERROR("elf_getdata");
Packit Service ac8aad
Packit Service ac8aad
		sec->index = elf_ndxscn(scn);
Packit Service ac8aad
Packit Service ac8aad
		log_debug("ndx %02d, data %p, size %zu, name %s\n",
Packit Service ac8aad
			sec->index, sec->data->d_buf, sec->data->d_size,
Packit Service ac8aad
			sec->name);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* Sanity check, one more call to elf_nextscn() should return NULL */
Packit Service ac8aad
	if (elf_nextscn(kelf->elf, scn))
Packit Service ac8aad
		ERROR("expected NULL");
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_create_symbol_list(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *symtab;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	unsigned int symbols_nr, index = 0;
Packit Service ac8aad
Packit Service ac8aad
	symtab = find_section_by_name(&kelf->sections, ".symtab");
Packit Service ac8aad
	if (!symtab)
Packit Service ac8aad
		ERROR("missing symbol table");
Packit Service ac8aad
Packit Service ac8aad
	symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
Packit Service ac8aad
Packit Service ac8aad
	log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr);
Packit Service ac8aad
Packit Service ac8aad
	while (symbols_nr--) {
Packit Service ac8aad
		ALLOC_LINK(sym, &kelf->symbols);
Packit Service ac8aad
Packit Service ac8aad
		sym->index = index;
Packit Service ac8aad
		if (!gelf_getsym(symtab->data, index, &sym->sym))
Packit Service ac8aad
			ERROR("gelf_getsym");
Packit Service ac8aad
		index++;
Packit Service ac8aad
Packit Service ac8aad
		sym->name = elf_strptr(kelf->elf, symtab->sh.sh_link,
Packit Service ac8aad
				       sym->sym.st_name);
Packit Service ac8aad
		if (!sym->name)
Packit Service ac8aad
			ERROR("elf_strptr");
Packit Service ac8aad
Packit Service ac8aad
		sym->type = GELF_ST_TYPE(sym->sym.st_info);
Packit Service ac8aad
		sym->bind = GELF_ST_BIND(sym->sym.st_info);
Packit Service ac8aad
Packit Service ac8aad
		if (sym->sym.st_shndx > SHN_UNDEF &&
Packit Service ac8aad
		    sym->sym.st_shndx < SHN_LORESERVE) {
Packit Service ac8aad
			sym->sec = find_section_by_index(&kelf->sections,
Packit Service ac8aad
					sym->sym.st_shndx);
Packit Service ac8aad
			if (!sym->sec)
Packit Service ac8aad
				ERROR("couldn't find section for symbol %s\n",
Packit Service ac8aad
					sym->name);
Packit Service ac8aad
Packit Service ac8aad
			if (sym->type == STT_SECTION) {
Packit Service ac8aad
				sym->sec->secsym = sym;
Packit Service ac8aad
				/* use the section name as the symbol name */
Packit Service ac8aad
				sym->name = sym->sec->name;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s",
Packit Service ac8aad
			sym->index, sym->type, sym->bind, sym->sym.st_shndx,
Packit Service ac8aad
			sym->name);
Packit Service ac8aad
		if (sym->sec)
Packit Service ac8aad
			log_debug(" -> %s", sym->sec->name);
Packit Service ac8aad
		log_debug("\n");
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/* Check which functions have fentry/mcount calls; save this info for later use. */
Packit Service ac8aad
static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->type != STT_FUNC || !sym->sec || !sym->sec->rela)
Packit Service ac8aad
			continue;
Packit Service ac8aad
#ifdef __powerpc64__
Packit Service ac8aad
		list_for_each_entry(rela, &sym->sec->rela->relas, list) {
Packit Service ac8aad
			if (!strcmp(rela->sym->name, "_mcount")) {
Packit Service ac8aad
				sym->has_func_profiling = 1;
Packit Service ac8aad
				break;
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
#else
Packit Service ac8aad
		rela = list_first_entry(&sym->sec->rela->relas, struct rela,
Packit Service ac8aad
					list);
Packit Service ac8aad
		if (rela->type != R_X86_64_NONE ||
Packit Service ac8aad
		    strcmp(rela->sym->name, "__fentry__"))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		sym->has_func_profiling = 1;
Packit Service ac8aad
#endif
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct kpatch_elf *kpatch_elf_open(const char *name)
Packit Service ac8aad
{
Packit Service ac8aad
	Elf *elf;
Packit Service ac8aad
	int fd;
Packit Service ac8aad
	struct kpatch_elf *kelf;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
Packit Service ac8aad
	fd = open(name, O_RDONLY);
Packit Service ac8aad
	if (fd == -1)
Packit Service ac8aad
		ERROR("open");
Packit Service ac8aad
Packit Service ac8aad
	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
Packit Service ac8aad
	if (!elf)
Packit Service ac8aad
		ERROR("elf_begin");
Packit Service ac8aad
Packit Service ac8aad
	kelf = malloc(sizeof(*kelf));
Packit Service ac8aad
	if (!kelf)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	memset(kelf, 0, sizeof(*kelf));
Packit Service ac8aad
	INIT_LIST_HEAD(&kelf->sections);
Packit Service ac8aad
	INIT_LIST_HEAD(&kelf->symbols);
Packit Service ac8aad
	INIT_LIST_HEAD(&kelf->strings);
Packit Service ac8aad
Packit Service ac8aad
	/* read and store section, symbol entries from file */
Packit Service ac8aad
	kelf->elf = elf;
Packit Service ac8aad
	kelf->fd = fd;
Packit Service ac8aad
	kpatch_create_section_list(kelf);
Packit Service ac8aad
	kpatch_create_symbol_list(kelf);
Packit Service ac8aad
Packit Service ac8aad
	/* for each rela section, read and store the rela entries */
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
		INIT_LIST_HEAD(&sec->relas);
Packit Service ac8aad
		kpatch_create_rela_list(kelf, sec);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	kpatch_find_func_profiling_calls(kelf);
Packit Service ac8aad
	return kelf;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_dump_kelf(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
	if (loglevel > DEBUG)
Packit Service ac8aad
		return;
Packit Service ac8aad
Packit Service ac8aad
	printf("\n=== Sections ===\n");
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		printf("%02d %s (%s)", sec->index, sec->name, status_str(sec->status));
Packit Service ac8aad
		if (is_rela_section(sec)) {
Packit Service ac8aad
			printf(", base-> %s\n", sec->base->name);
Packit Service ac8aad
			/* skip .debug_* sections */
Packit Service ac8aad
			if (is_debug_section(sec))
Packit Service ac8aad
				goto next;
Packit Service ac8aad
			printf("rela section expansion\n");
Packit Service ac8aad
			list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
				printf("sym %d, offset %d, type %d, %s %s %d\n",
Packit Service ac8aad
				       rela->sym->index, rela->offset,
Packit Service ac8aad
				       rela->type, rela->sym->name,
Packit Service ac8aad
				       (rela->addend < 0)?"-":"+",
Packit Service ac8aad
				       abs(rela->addend));
Packit Service ac8aad
			}
Packit Service ac8aad
		} else {
Packit Service ac8aad
			if (sec->sym)
Packit Service ac8aad
				printf(", sym-> %s", sec->sym->name);
Packit Service ac8aad
			if (sec->secsym)
Packit Service ac8aad
				printf(", secsym-> %s", sec->secsym->name);
Packit Service ac8aad
			if (sec->rela)
Packit Service ac8aad
				printf(", rela-> %s", sec->rela->name);
Packit Service ac8aad
		}
Packit Service ac8aad
next:
Packit Service ac8aad
		printf("\n");
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	printf("\n=== Symbols ===\n");
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		printf("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)",
Packit Service ac8aad
			sym->index, sym->type, sym->bind, sym->sym.st_shndx,
Packit Service ac8aad
			sym->name, status_str(sym->status));
Packit Service ac8aad
		if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT))
Packit Service ac8aad
			printf(" -> %s", sym->sec->name);
Packit Service ac8aad
		printf("\n");
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_null_sym(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	return !strlen(sym->name);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_file_sym(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	return sym->type == STT_FILE;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_local_func_sym(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	return sym->bind == STB_LOCAL && sym->type == STT_FUNC;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
int is_local_sym(struct symbol *sym)
Packit Service ac8aad
{
Packit Service ac8aad
	return sym->bind == STB_LOCAL;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void print_strtab(char *buf, size_t size)
Packit Service ac8aad
{
Packit Service ac8aad
	size_t i;
Packit Service ac8aad
Packit Service ac8aad
	for (i = 0; i < size; i++) {
Packit Service ac8aad
		if (buf[i] == 0)
Packit Service ac8aad
			printf("\\0");
Packit Service ac8aad
		else
Packit Service ac8aad
			printf("%c",buf[i]);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_create_shstrtab(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *shstrtab, *sec;
Packit Service ac8aad
	size_t size, offset, len;
Packit Service ac8aad
	char *buf;
Packit Service ac8aad
Packit Service ac8aad
	shstrtab = find_section_by_name(&kelf->sections, ".shstrtab");
Packit Service ac8aad
	if (!shstrtab)
Packit Service ac8aad
		ERROR("find_section_by_name");
Packit Service ac8aad
Packit Service ac8aad
	/* determine size of string table */
Packit Service ac8aad
	size = 1; /* for initial NULL terminator */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list)
Packit Service ac8aad
		size += strlen(sec->name) + 1; /* include NULL terminator */
Packit Service ac8aad
Packit Service ac8aad
	/* allocate data buffer */
Packit Service ac8aad
	buf = malloc(size);
Packit Service ac8aad
	if (!buf)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	memset(buf, 0, size);
Packit Service ac8aad
Packit Service ac8aad
	/* populate string table and link with section header */
Packit Service ac8aad
	offset = 1;
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		len = strlen(sec->name) + 1;
Packit Service ac8aad
		sec->sh.sh_name = offset;
Packit Service ac8aad
		memcpy(buf + offset, sec->name, len);
Packit Service ac8aad
		offset += len;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (offset != size)
Packit Service ac8aad
		ERROR("shstrtab size mismatch");
Packit Service ac8aad
Packit Service ac8aad
	shstrtab->data->d_buf = buf;
Packit Service ac8aad
	shstrtab->data->d_size = size;
Packit Service ac8aad
Packit Service ac8aad
	if (loglevel <= DEBUG) {
Packit Service ac8aad
		printf("shstrtab: ");
Packit Service ac8aad
		print_strtab(buf, size);
Packit Service ac8aad
		printf("\n");
Packit Service ac8aad
Packit Service ac8aad
		list_for_each_entry(sec, &kelf->sections, list)
Packit Service ac8aad
			printf("%s @ shstrtab offset %d\n",
Packit Service ac8aad
			       sec->name, sec->sh.sh_name);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_create_strtab(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *strtab;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	size_t size = 0, offset = 0, len;
Packit Service ac8aad
	char *buf;
Packit Service ac8aad
Packit Service ac8aad
	strtab = find_section_by_name(&kelf->sections, ".strtab");
Packit Service ac8aad
	if (!strtab)
Packit Service ac8aad
		ERROR("find_section_by_name");
Packit Service ac8aad
Packit Service ac8aad
	/* determine size of string table */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->type == STT_SECTION)
Packit Service ac8aad
			continue;
Packit Service ac8aad
		size += strlen(sym->name) + 1; /* include NULL terminator */
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	/* allocate data buffer */
Packit Service ac8aad
	buf = malloc(size);
Packit Service ac8aad
	if (!buf)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	memset(buf, 0, size);
Packit Service ac8aad
Packit Service ac8aad
	/* populate string table and link with section header */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		if (sym->type == STT_SECTION) {
Packit Service ac8aad
			sym->sym.st_name = 0;
Packit Service ac8aad
			continue;
Packit Service ac8aad
		}
Packit Service ac8aad
		len = strlen(sym->name) + 1;
Packit Service ac8aad
		sym->sym.st_name = offset;
Packit Service ac8aad
		memcpy(buf + offset, sym->name, len);
Packit Service ac8aad
		offset += len;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (offset != size)
Packit Service ac8aad
		ERROR("shstrtab size mismatch");
Packit Service ac8aad
Packit Service ac8aad
	strtab->data->d_buf = buf;
Packit Service ac8aad
	strtab->data->d_size = size;
Packit Service ac8aad
Packit Service ac8aad
	if (loglevel <= DEBUG) {
Packit Service ac8aad
		printf("strtab: ");
Packit Service ac8aad
		print_strtab(buf, size);
Packit Service ac8aad
		printf("\n");
Packit Service ac8aad
Packit Service ac8aad
		list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
			printf("%s @ strtab offset %d\n",
Packit Service ac8aad
			       sym->name, sym->sym.st_name);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_create_symtab(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *symtab;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	char *buf;
Packit Service ac8aad
	size_t size;
Packit Service ac8aad
	int nr = 0, offset = 0, nr_local = 0;
Packit Service ac8aad
Packit Service ac8aad
	symtab = find_section_by_name(&kelf->sections, ".symtab");
Packit Service ac8aad
	if (!symtab)
Packit Service ac8aad
		ERROR("find_section_by_name");
Packit Service ac8aad
Packit Service ac8aad
	/* count symbols */
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list)
Packit Service ac8aad
		nr++;
Packit Service ac8aad
Packit Service ac8aad
	/* create new symtab buffer */
Packit Service ac8aad
	size = nr * symtab->sh.sh_entsize;
Packit Service ac8aad
	buf = malloc(size);
Packit Service ac8aad
	if (!buf)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	memset(buf, 0, size);
Packit Service ac8aad
Packit Service ac8aad
	offset = 0;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize);
Packit Service ac8aad
		offset += symtab->sh.sh_entsize;
Packit Service ac8aad
Packit Service ac8aad
		if (is_local_sym(sym))
Packit Service ac8aad
			nr_local++;
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	symtab->data->d_buf = buf;
Packit Service ac8aad
	symtab->data->d_size = size;
Packit Service ac8aad
Packit Service ac8aad
	/* update symtab section header */
Packit Service ac8aad
	symtab->sh.sh_link = find_section_by_name(&kelf->sections, ".strtab")->index;
Packit Service ac8aad
	symtab->sh.sh_info = nr_local;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
struct section *create_section_pair(struct kpatch_elf *kelf, char *name,
Packit Service ac8aad
                                    int entsize, int nr)
Packit Service ac8aad
{
Packit Service ac8aad
	char *relaname;
Packit Service ac8aad
	struct section *sec, *relasec;
Packit Service ac8aad
	int size = entsize * nr;
Packit Service ac8aad
Packit Service ac8aad
	relaname = malloc(strlen(name) + strlen(".rela") + 1);
Packit Service ac8aad
	if (!relaname)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	strcpy(relaname, ".rela");
Packit Service ac8aad
	strcat(relaname, name);
Packit Service ac8aad
Packit Service ac8aad
	/* allocate text section resources */
Packit Service ac8aad
	ALLOC_LINK(sec, &kelf->sections);
Packit Service ac8aad
	sec->name = name;
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_buf = malloc(size);
Packit Service ac8aad
	if (!sec->data->d_buf)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
	memset(sec->data->d_buf, 0, size);
Packit Service ac8aad
	sec->data->d_size = size;
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 = entsize;
Packit Service ac8aad
	sec->sh.sh_addralign = 8;
Packit Service ac8aad
	sec->sh.sh_flags = SHF_ALLOC;
Packit Service ac8aad
	sec->sh.sh_size = size;
Packit Service ac8aad
Packit Service ac8aad
	/* allocate rela section resources */
Packit Service ac8aad
	ALLOC_LINK(relasec, &kelf->sections);
Packit Service ac8aad
	relasec->name = relaname;
Packit Service ac8aad
	relasec->base = sec;
Packit Service ac8aad
	INIT_LIST_HEAD(&relasec->relas);
Packit Service ac8aad
Packit Service ac8aad
	/* set data, buffers generated by kpatch_rebuild_rela_section_data() */
Packit Service ac8aad
	relasec->data = malloc(sizeof(*relasec->data));
Packit Service ac8aad
	if (!relasec->data)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
Packit Service ac8aad
	/* set section header */
Packit Service ac8aad
	relasec->sh.sh_type = SHT_RELA;
Packit Service ac8aad
	relasec->sh.sh_entsize = sizeof(GElf_Rela);
Packit Service ac8aad
	relasec->sh.sh_addralign = 8;
Packit Service ac8aad
Packit Service ac8aad
	/* set text rela section pointer */
Packit Service ac8aad
	sec->rela = relasec;
Packit Service ac8aad
Packit Service ac8aad
	return sec;
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_remove_and_free_section(struct kpatch_elf *kelf, char *secname)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec, *safesec;
Packit Service ac8aad
	struct rela *rela, *saferela;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
Packit Service ac8aad
		if (strcmp(secname, sec->name))
Packit Service ac8aad
			continue;
Packit Service ac8aad
Packit Service ac8aad
		if (is_rela_section(sec)) {
Packit Service ac8aad
			list_for_each_entry_safe(rela, saferela, &sec->relas, list) {
Packit Service ac8aad
				list_del(&rela->list);
Packit Service ac8aad
				memset(rela, 0, sizeof(*rela));
Packit Service ac8aad
				free(rela);
Packit Service ac8aad
			}
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		/*
Packit Service ac8aad
		 * Remove the STT_SECTION symbol from the symtab,
Packit Service ac8aad
		 * otherwise when we remove the section we'll end up
Packit Service ac8aad
		 * with UNDEF section symbols in the symtab.
Packit Service ac8aad
		 */
Packit Service ac8aad
		if (!is_rela_section(sec) && sec->secsym) {
Packit Service ac8aad
			list_del(&sec->secsym->list);
Packit Service ac8aad
			memset(sec->secsym, 0, sizeof(*sec->secsym));
Packit Service ac8aad
			free(sec->secsym);
Packit Service ac8aad
		}
Packit Service ac8aad
Packit Service ac8aad
		list_del(&sec->list);
Packit Service ac8aad
		memset(sec, 0, sizeof(*sec));
Packit Service ac8aad
		free(sec);
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_reindex_elements(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	struct symbol *sym;
Packit Service ac8aad
	unsigned int index;
Packit Service ac8aad
Packit Service ac8aad
	index = 1; /* elf write function handles NULL section 0 */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list)
Packit Service ac8aad
		sec->index = index++;
Packit Service ac8aad
Packit Service ac8aad
	index = 0;
Packit Service ac8aad
	list_for_each_entry(sym, &kelf->symbols, list) {
Packit Service ac8aad
		sym->index = index++;
Packit Service ac8aad
		if (sym->sec)
Packit Service ac8aad
			sym->sym.st_shndx = sym->sec->index;
Packit Service ac8aad
		else if (sym->sym.st_shndx != SHN_ABS &&
Packit Service ac8aad
			 sym->sym.st_shndx != SHN_LIVEPATCH)
Packit Service ac8aad
			sym->sym.st_shndx = SHN_UNDEF;
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_rebuild_rela_section_data(struct section *sec)
Packit Service ac8aad
{
Packit Service ac8aad
	struct rela *rela;
Packit Service ac8aad
	int nr = 0, index = 0, size;
Packit Service ac8aad
	GElf_Rela *relas;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(rela, &sec->relas, list)
Packit Service ac8aad
		nr++;
Packit Service ac8aad
Packit Service ac8aad
	size = nr * sizeof(*relas);
Packit Service ac8aad
	relas = malloc(size);
Packit Service ac8aad
	if (!relas)
Packit Service ac8aad
		ERROR("malloc");
Packit Service ac8aad
Packit Service ac8aad
	sec->data->d_buf = relas;
Packit Service ac8aad
	sec->data->d_size = size;
Packit Service ac8aad
	/* d_type remains ELF_T_RELA */
Packit Service ac8aad
Packit Service ac8aad
	sec->sh.sh_size = size;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry(rela, &sec->relas, list) {
Packit Service ac8aad
		relas[index].r_offset = rela->offset;
Packit Service ac8aad
		relas[index].r_addend = rela->addend;
Packit Service ac8aad
		relas[index].r_info = GELF_R_INFO(rela->sym->index, rela->type);
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 rebuilt rela section");
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile)
Packit Service ac8aad
{
Packit Service ac8aad
	int fd;
Packit Service ac8aad
	struct section *sec;
Packit Service ac8aad
	Elf *elfout;
Packit Service ac8aad
	GElf_Ehdr eh, ehout;
Packit Service ac8aad
	Elf_Scn *scn;
Packit Service ac8aad
	Elf_Data *data;
Packit Service ac8aad
	GElf_Shdr sh;
Packit Service ac8aad
Packit Service ac8aad
	/* TODO make this argv */
Packit Service ac8aad
	fd = creat(outfile, 0777);
Packit Service ac8aad
	if (fd == -1)
Packit Service ac8aad
		ERROR("creat");
Packit Service ac8aad
Packit Service ac8aad
	elfout = elf_begin(fd, ELF_C_WRITE, NULL);
Packit Service ac8aad
	if (!elfout)
Packit Service ac8aad
		ERROR("elf_begin");
Packit Service ac8aad
Packit Service ac8aad
	if (!gelf_newehdr(elfout, gelf_getclass(kelf->elf)))
Packit Service ac8aad
		ERROR("gelf_newehdr");
Packit Service ac8aad
Packit Service ac8aad
	if (!gelf_getehdr(elfout, &ehout))
Packit Service ac8aad
		ERROR("gelf_getehdr");
Packit Service ac8aad
Packit Service ac8aad
	if (!gelf_getehdr(elf, &eh))
Packit Service ac8aad
		ERROR("gelf_getehdr");
Packit Service ac8aad
Packit Service ac8aad
	memset(&ehout, 0, sizeof(ehout));
Packit Service ac8aad
	ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA];
Packit Service ac8aad
	ehout.e_machine = eh.e_machine;
Packit Service ac8aad
	ehout.e_type = eh.e_type;
Packit Service ac8aad
	ehout.e_version = EV_CURRENT;
Packit Service ac8aad
	ehout.e_shstrndx = find_section_by_name(&kelf->sections, ".shstrtab")->index;
Packit Service ac8aad
Packit Service ac8aad
	/* add changed sections */
Packit Service ac8aad
	list_for_each_entry(sec, &kelf->sections, list) {
Packit Service ac8aad
		scn = elf_newscn(elfout);
Packit Service ac8aad
		if (!scn)
Packit Service ac8aad
			ERROR("elf_newscn");
Packit Service ac8aad
Packit Service ac8aad
		data = elf_newdata(scn);
Packit Service ac8aad
		if (!data)
Packit Service ac8aad
			ERROR("elf_newdata");
Packit Service ac8aad
Packit Service ac8aad
		if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY))
Packit Service ac8aad
			ERROR("elf_flagdata");
Packit Service ac8aad
Packit Service ac8aad
		data->d_type = sec->data->d_type;
Packit Service ac8aad
		data->d_buf = sec->data->d_buf;
Packit Service ac8aad
		data->d_size = sec->data->d_size;
Packit Service ac8aad
Packit Service ac8aad
		if(!gelf_getshdr(scn, &sh))
Packit Service ac8aad
			ERROR("gelf_getshdr");
Packit Service ac8aad
Packit Service ac8aad
		sh = sec->sh;
Packit Service ac8aad
Packit Service ac8aad
		if (!gelf_update_shdr(scn, &sh))
Packit Service ac8aad
			ERROR("gelf_update_shdr");
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	if (!gelf_update_ehdr(elfout, &ehout))
Packit Service ac8aad
		ERROR("gelf_update_ehdr");
Packit Service ac8aad
Packit Service ac8aad
	if (elf_update(elfout, ELF_C_WRITE) < 0) {
Packit Service ac8aad
		printf("%s\n",elf_errmsg(-1));
Packit Service ac8aad
		ERROR("elf_update");
Packit Service ac8aad
	}
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
/*
Packit Service ac8aad
 * While this is a one-shot program without a lot of proper cleanup in case
Packit Service ac8aad
 * of an error, this function serves a debugging purpose: to break down and
Packit Service ac8aad
 * zero data structures we shouldn't be accessing anymore.  This should
Packit Service ac8aad
 * help cause an immediate and obvious issue when a logic error leads to
Packit Service ac8aad
 * accessing data that is not intended to be accessed past a particular point.
Packit Service ac8aad
 */
Packit Service ac8aad
void kpatch_elf_teardown(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	struct section *sec, *safesec;
Packit Service ac8aad
	struct symbol *sym, *safesym;
Packit Service ac8aad
	struct rela *rela, *saferela;
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
Packit Service ac8aad
		if (is_rela_section(sec)) {
Packit Service ac8aad
			list_for_each_entry_safe(rela, saferela, &sec->relas, list) {
Packit Service ac8aad
				memset(rela, 0, sizeof(*rela));
Packit Service ac8aad
				free(rela);
Packit Service ac8aad
			}
Packit Service ac8aad
			memset(sec, 0, sizeof(*sec));
Packit Service ac8aad
			free(sec);
Packit Service ac8aad
		}
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
Packit Service ac8aad
		memset(sym, 0, sizeof(*sym));
Packit Service ac8aad
		free(sym);
Packit Service ac8aad
	}
Packit Service ac8aad
Packit Service ac8aad
	INIT_LIST_HEAD(&kelf->sections);
Packit Service ac8aad
	INIT_LIST_HEAD(&kelf->symbols);
Packit Service ac8aad
}
Packit Service ac8aad
Packit Service ac8aad
void kpatch_elf_free(struct kpatch_elf *kelf)
Packit Service ac8aad
{
Packit Service ac8aad
	elf_end(kelf->elf);
Packit Service ac8aad
	close(kelf->fd);
Packit Service ac8aad
	memset(kelf, 0, sizeof(*kelf));
Packit Service ac8aad
	free(kelf);
Packit Service ac8aad
}