|
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 |
}
|