Blame elfcreator.c

Packit Service 154b7f
/*
Packit Service 154b7f
 * SPDX-License-Identifier: GPL-2.0-only
Packit Service 154b7f
 *
Packit Service 154b7f
 * Copyright 2009 Red Hat, Inc.
Packit Service 154b7f
 *
Packit Service 154b7f
 * Author: Peter Jones <pjones@redhat.com>
Packit Service 154b7f
 */
Packit Service 154b7f
#include <dlfcn.h>
Packit Service 154b7f
#include <gelf.h>
Packit Service 154b7f
#include <stdio.h>
Packit Service 154b7f
#include <strings.h>
Packit Service 154b7f
#include <string.h>
Packit Service 154b7f
#include <fcntl.h>
Packit Service 154b7f
#include <stdlib.h>
Packit Service 154b7f
#include <unistd.h>
Packit Service 154b7f
#include <assert.h>
Packit Service 154b7f
Packit Service 154b7f
#include "elfcreator.h"
Packit Service 154b7f
Packit Service 154b7f
struct elf_creator {
Packit Service 154b7f
	const char *path;
Packit Service 154b7f
	int fd;
Packit Service 154b7f
Packit Service 154b7f
	Elf *elf;
Packit Service 154b7f
	GElf_Ehdr *ehdr, ehdr_mem;
Packit Service 154b7f
Packit Service 154b7f
	Elf *oldelf;
Packit Service 154b7f
	/* just because we have to look this up /so/ often... */
Packit Service 154b7f
	Elf_Scn *dynscn;
Packit Service 154b7f
	GElf_Shdr *dynshdr, dynshdr_mem;
Packit Service 154b7f
	Elf_Data *dyndata;
Packit Service 154b7f
};
Packit Service 154b7f
Packit Service 154b7f
static void clear(ElfCreator *ctor, int do_unlink)
Packit Service 154b7f
{
Packit Service 154b7f
	if (do_unlink) {
Packit Service 154b7f
		if (ctor->elf)
Packit Service 154b7f
			elf_end(ctor->elf);
Packit Service 154b7f
		if (ctor->fd >= 0)
Packit Service 154b7f
			close(ctor->fd);
Packit Service 154b7f
		if (ctor->path)
Packit Service 154b7f
			unlink(ctor->path);
Packit Service 154b7f
	} else {
Packit Service 154b7f
		if (ctor->elf) {
Packit Service 154b7f
			elf_update(ctor->elf, ELF_C_WRITE_MMAP);
Packit Service 154b7f
			elf_end(ctor->elf);
Packit Service 154b7f
		}
Packit Service 154b7f
		if (ctor->fd >= 0)
Packit Service 154b7f
			close(ctor->fd);
Packit Service 154b7f
	}
Packit Service 154b7f
	memset(ctor, '\0', sizeof(*ctor));
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
ElfCreator *elfcreator_begin(char *path, Elf *elf) {
Packit Service 154b7f
	ElfCreator *ctor = NULL;
Packit Service 154b7f
	GElf_Ehdr ehdr_mem, *ehdr;
Packit Service 154b7f
Packit Service 154b7f
	if (!(ctor = calloc(1, sizeof(*ctor))))
Packit Service 154b7f
		return NULL;
Packit Service 154b7f
Packit Service 154b7f
	clear(ctor, 0);
Packit Service 154b7f
Packit Service 154b7f
	ctor->path = path;
Packit Service 154b7f
	ctor->oldelf = elf;
Packit Service 154b7f
Packit Service 154b7f
	ehdr = gelf_getehdr(elf, &ehdr_mem);
Packit Service 154b7f
Packit Service 154b7f
	if ((ctor->fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0755)) < 0) {
Packit Service 154b7f
err:
Packit Service 154b7f
		clear(ctor, 1);
Packit Service 154b7f
		free(ctor);
Packit Service 154b7f
		return NULL;
Packit Service 154b7f
	}
Packit Service 154b7f
Packit Service 154b7f
	if (!(ctor->elf = elf_begin(ctor->fd, ELF_C_WRITE_MMAP, elf)))
Packit Service 154b7f
		goto err;
Packit Service 154b7f
Packit Service 154b7f
	gelf_newehdr(ctor->elf, gelf_getclass(elf));
Packit Service 154b7f
	gelf_update_ehdr(ctor->elf, ehdr);
Packit Service 154b7f
Packit Service 154b7f
	if (!(ctor->ehdr = gelf_getehdr(ctor->elf, &ctor->ehdr_mem)))
Packit Service 154b7f
		goto err;
Packit Service 154b7f
Packit Service 154b7f
	return ctor;
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static Elf_Scn *get_scn_by_type(ElfCreator *ctor, Elf64_Word sh_type)
Packit Service 154b7f
{
Packit Service 154b7f
	Elf_Scn *scn = NULL;
Packit Service 154b7f
Packit Service 154b7f
	while ((scn = elf_nextscn(ctor->elf, scn)) != NULL) {
Packit Service 154b7f
		GElf_Shdr *shdr, shdr_mem;
Packit Service 154b7f
Packit Service 154b7f
		shdr = gelf_getshdr(scn, &shdr_mem);
Packit Service 154b7f
		if (shdr->sh_type == sh_type)
Packit Service 154b7f
			return scn;
Packit Service 154b7f
	}
Packit Service 154b7f
	return NULL;
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static void update_dyn_cache(ElfCreator *ctor)
Packit Service 154b7f
{
Packit Service 154b7f
	ctor->dynscn = get_scn_by_type(ctor, SHT_DYNAMIC);
Packit Service 154b7f
	if (ctor->dynscn == NULL)
Packit Service 154b7f
		return;
Packit Service 154b7f
Packit Service 154b7f
	ctor->dynshdr = gelf_getshdr(ctor->dynscn, &ctor->dynshdr_mem);
Packit Service 154b7f
	ctor->dyndata = elf_getdata(ctor->dynscn, NULL);
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
void elfcreator_copy_scn(ElfCreator *ctor, Elf *src, Elf_Scn *scn)
Packit Service 154b7f
{
Packit Service 154b7f
	Elf_Scn *newscn;
Packit Service 154b7f
	Elf_Data *indata, *outdata;
Packit Service 154b7f
	GElf_Shdr *oldshdr, oldshdr_mem;
Packit Service 154b7f
	GElf_Shdr *newshdr, newshdr_mem;
Packit Service 154b7f
Packit Service 154b7f
	newscn = elf_newscn(ctor->elf);
Packit Service 154b7f
	newshdr = gelf_getshdr(newscn, &newshdr_mem);
Packit Service 154b7f
Packit Service 154b7f
	oldshdr = gelf_getshdr(scn, &oldshdr_mem);
Packit Service 154b7f
Packit Service 154b7f
	memmove(newshdr, oldshdr, sizeof(*newshdr));
Packit Service 154b7f
	gelf_update_shdr(newscn, newshdr);
Packit Service 154b7f
Packit Service 154b7f
	indata = NULL;
Packit Service 154b7f
	while ((indata = elf_getdata(scn, indata)) != NULL) {
Packit Service 154b7f
		outdata = elf_newdata(newscn);
Packit Service 154b7f
		*outdata = *indata;
Packit Service 154b7f
	}
Packit Service 154b7f
	if (newshdr->sh_type == SHT_DYNAMIC)
Packit Service 154b7f
		update_dyn_cache(ctor);
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static GElf_Dyn *get_dyn_by_tag(ElfCreator *ctor, Elf64_Sxword d_tag,
Packit Service 154b7f
				GElf_Dyn *mem, size_t *idx)
Packit Service 154b7f
{
Packit Service 154b7f
	size_t cnt;
Packit Service 154b7f
Packit Service 154b7f
	if (!ctor->dyndata)
Packit Service 154b7f
		return NULL;
Packit Service 154b7f
Packit Service 154b7f
	for (cnt = 1; cnt < ctor->dynshdr->sh_size / ctor->dynshdr->sh_entsize;
Packit Service 154b7f
			cnt++) {
Packit Service 154b7f
		GElf_Dyn *dyn;
Packit Service 154b7f
Packit Service 154b7f
		if ((dyn = gelf_getdyn(ctor->dyndata, cnt, mem)) == NULL)
Packit Service 154b7f
			break;
Packit Service 154b7f
Packit Service 154b7f
		if (dyn->d_tag == d_tag) {
Packit Service 154b7f
			*idx = cnt;
Packit Service 154b7f
			return dyn;
Packit Service 154b7f
		}
Packit Service 154b7f
	}
Packit Service 154b7f
	return NULL;
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static void remove_dyn(ElfCreator *ctor, size_t idx)
Packit Service 154b7f
{
Packit Service 154b7f
	size_t cnt;
Packit Service 154b7f
Packit Service 154b7f
	for (cnt = idx; cnt < ctor->dynshdr->sh_size/ctor->dynshdr->sh_entsize;
Packit Service 154b7f
			cnt++) {
Packit Service 154b7f
		GElf_Dyn *dyn, dyn_mem;
Packit Service 154b7f
Packit Service 154b7f
		if (cnt+1 == ctor->dynshdr->sh_size/ctor->dynshdr->sh_entsize) {
Packit Service 154b7f
			memset(&dyn_mem, '\0', sizeof(dyn_mem));
Packit Service 154b7f
			gelf_update_dyn(ctor->dyndata, cnt, &dyn_mem);
Packit Service 154b7f
			break;
Packit Service 154b7f
		}
Packit Service 154b7f
Packit Service 154b7f
		dyn = gelf_getdyn(ctor->dyndata, cnt+1, &dyn_mem);
Packit Service 154b7f
		gelf_update_dyn(ctor->dyndata, cnt, dyn);
Packit Service 154b7f
	}
Packit Service 154b7f
	ctor->dynshdr->sh_size--;
Packit Service 154b7f
	gelf_update_shdr(ctor->dynscn, ctor->dynshdr);
Packit Service 154b7f
	update_dyn_cache(ctor);
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
typedef void (*dyn_fixup_fn)(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn);
Packit Service 154b7f
Packit Service 154b7f
static void generic_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn)
Packit Service 154b7f
{
Packit Service 154b7f
	GElf_Shdr *shdr, shdr_mem;
Packit Service 154b7f
	GElf_Dyn *dyn, dyn_mem;
Packit Service 154b7f
	size_t idx = 0;
Packit Service 154b7f
Packit Service 154b7f
	dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx);
Packit Service 154b7f
	shdr = gelf_getshdr(scn, &shdr_mem);
Packit Service 154b7f
	if (shdr) {
Packit Service 154b7f
		dyn->d_un.d_ptr = shdr->sh_addr;
Packit Service 154b7f
		gelf_update_dyn(ctor->dyndata, idx, dyn);
Packit Service 154b7f
	} else {
Packit Service 154b7f
		remove_dyn(ctor, idx);
Packit Service 154b7f
	}
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static void rela_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn)
Packit Service 154b7f
{
Packit Service 154b7f
	GElf_Shdr *shdr, shdr_mem;
Packit Service 154b7f
	GElf_Dyn *dyn, dyn_mem;
Packit Service 154b7f
	size_t idx = 0;
Packit Service 154b7f
Packit Service 154b7f
	dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx);
Packit Service 154b7f
	shdr = gelf_getshdr(scn, &shdr_mem);
Packit Service 154b7f
	if (shdr) {
Packit Service 154b7f
		dyn->d_un.d_ptr = shdr->sh_addr;
Packit Service 154b7f
		gelf_update_dyn(ctor->dyndata, idx, dyn);
Packit Service 154b7f
	} else {
Packit Service 154b7f
		remove_dyn(ctor, idx);
Packit Service 154b7f
		dyn = get_dyn_by_tag(ctor, DT_RELASZ, &dyn_mem, &idx);
Packit Service 154b7f
		if (dyn) {
Packit Service 154b7f
			dyn->d_un.d_val = 0;
Packit Service 154b7f
			gelf_update_dyn(ctor->dyndata, idx, dyn);
Packit Service 154b7f
		}
Packit Service 154b7f
	}
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static void rel_dyn_fixup_fn(ElfCreator *ctor, Elf64_Sxword d_tag, Elf_Scn *scn)
Packit Service 154b7f
{
Packit Service 154b7f
	GElf_Shdr *shdr, shdr_mem;
Packit Service 154b7f
	GElf_Dyn *dyn, dyn_mem;
Packit Service 154b7f
	size_t idx = 0;
Packit Service 154b7f
Packit Service 154b7f
	dyn = get_dyn_by_tag(ctor, d_tag, &dyn_mem, &idx);
Packit Service 154b7f
	shdr = gelf_getshdr(scn, &shdr_mem);
Packit Service 154b7f
	if (shdr) {
Packit Service 154b7f
		dyn->d_un.d_ptr = shdr->sh_addr;
Packit Service 154b7f
		gelf_update_dyn(ctor->dyndata, idx, dyn);
Packit Service 154b7f
	} else {
Packit Service 154b7f
		remove_dyn(ctor, idx);
Packit Service 154b7f
		dyn = get_dyn_by_tag(ctor, DT_RELSZ, &dyn_mem, &idx);
Packit Service 154b7f
		if (dyn) {
Packit Service 154b7f
			dyn->d_un.d_val = 0;
Packit Service 154b7f
			gelf_update_dyn(ctor->dyndata, idx, dyn);
Packit Service 154b7f
		}
Packit Service 154b7f
	}
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
static void fixup_dynamic(ElfCreator *ctor)
Packit Service 154b7f
{
Packit Service 154b7f
	struct {
Packit Service 154b7f
		Elf64_Sxword d_tag;
Packit Service 154b7f
		Elf64_Word sh_type;
Packit Service 154b7f
		dyn_fixup_fn fn;
Packit Service 154b7f
	} fixups[] = {
Packit Service 154b7f
		{ DT_HASH, SHT_HASH, NULL },
Packit Service 154b7f
		{ DT_STRTAB, SHT_STRTAB, NULL },
Packit Service 154b7f
		{ DT_SYMTAB, SHT_SYMTAB, NULL },
Packit Service 154b7f
		{ DT_RELA, SHT_RELA, rela_dyn_fixup_fn},
Packit Service 154b7f
		{ DT_REL, SHT_REL, rel_dyn_fixup_fn},
Packit Service 154b7f
		{ DT_GNU_HASH, SHT_GNU_HASH, NULL },
Packit Service 154b7f
		{ DT_NULL, SHT_NULL, NULL }
Packit Service 154b7f
	};
Packit Service 154b7f
	int i;
Packit Service 154b7f
Packit Service 154b7f
	for (i = 0; fixups[i].d_tag != DT_NULL; i++) {
Packit Service 154b7f
		Elf_Scn *scn;
Packit Service 154b7f
Packit Service 154b7f
		scn = get_scn_by_type(ctor, fixups[i].sh_type);
Packit Service 154b7f
		if (fixups[i].fn)
Packit Service 154b7f
			fixups[i].fn(ctor, fixups[i].d_tag, scn);
Packit Service 154b7f
		else
Packit Service 154b7f
			generic_dyn_fixup_fn(ctor, fixups[i].d_tag, scn);
Packit Service 154b7f
	}
Packit Service 154b7f
}
Packit Service 154b7f
Packit Service 154b7f
void elfcreator_end(ElfCreator *ctor)
Packit Service 154b7f
{
Packit Service 154b7f
	GElf_Phdr phdr_mem, *phdr;
Packit Service 154b7f
	int m,n;
Packit Service 154b7f
Packit Service 154b7f
	for (m = 0; (phdr = gelf_getphdr(ctor->oldelf, m, &phdr_mem)) != NULL; m++)
Packit Service 154b7f
		/* XXX this should check if an entry is needed */;
Packit Service 154b7f
Packit Service 154b7f
	gelf_newphdr(ctor->elf, m);
Packit Service 154b7f
	elf_update(ctor->elf, ELF_C_NULL);
Packit Service 154b7f
	update_dyn_cache(ctor);
Packit Service 154b7f
Packit Service 154b7f
	for (n = 0; n < m; n++) {
Packit Service 154b7f
		/* XXX this should check if an entry is needed */
Packit Service 154b7f
		phdr = gelf_getphdr(ctor->oldelf, n, &phdr_mem);
Packit Service 154b7f
		if (ctor->dynshdr && phdr->p_type == PT_DYNAMIC)
Packit Service 154b7f
			phdr->p_offset = ctor->dynshdr->sh_offset;
Packit Service 154b7f
Packit Service 154b7f
		gelf_update_phdr(ctor->elf, n, phdr);
Packit Service 154b7f
	}
Packit Service 154b7f
Packit Service 154b7f
	fixup_dynamic(ctor);
Packit Service 154b7f
Packit Service 154b7f
	clear(ctor, 0);
Packit Service 154b7f
	free(ctor);
Packit Service 154b7f
}