mirror of
https://github.com/dynup/kpatch
synced 2025-02-21 12:26:52 +00:00
Merge pull request #601 from flaming-toast/kpatch-elf
Introduce a common kpatch-elf "api"
This commit is contained in:
commit
f678f31898
@ -4,8 +4,10 @@ CFLAGS += -I../kmod/patch -Iinsn -Wall -g -Werror
|
|||||||
LDFLAGS += -lelf
|
LDFLAGS += -lelf
|
||||||
|
|
||||||
TARGETS = create-diff-object
|
TARGETS = create-diff-object
|
||||||
OBJS = create-diff-object.o lookup.o insn/insn.o insn/inat.o
|
OBJS = create-diff-object.o kpatch-elf.o \
|
||||||
SOURCES = create-diff-object.c lookup.c insn/insn.c insn/inat.c
|
lookup.o insn/insn.o insn/inat.o
|
||||||
|
SOURCES = create-diff-object.c kpatch-elf.c \
|
||||||
|
lookup.c insn/insn.c insn/inat.c
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
@ -50,9 +50,7 @@
|
|||||||
#include "lookup.h"
|
#include "lookup.h"
|
||||||
#include "asm/insn.h"
|
#include "asm/insn.h"
|
||||||
#include "kpatch-patch.h"
|
#include "kpatch-patch.h"
|
||||||
|
#include "kpatch-elf.h"
|
||||||
#define ERROR(format, ...) \
|
|
||||||
error(1, 0, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define DIFF_FATAL(format, ...) \
|
#define DIFF_FATAL(format, ...) \
|
||||||
({ \
|
({ \
|
||||||
@ -60,478 +58,21 @@
|
|||||||
error(2, 0, "unreconcilable difference"); \
|
error(2, 0, "unreconcilable difference"); \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__)
|
|
||||||
#define log_normal(format, ...) log(NORMAL, "%s: " format, childobj, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define log(level, format, ...) \
|
|
||||||
({ \
|
|
||||||
if (loglevel <= (level)) \
|
|
||||||
printf(format, ##__VA_ARGS__); \
|
|
||||||
})
|
|
||||||
|
|
||||||
char *childobj;
|
char *childobj;
|
||||||
|
|
||||||
enum loglevel {
|
enum loglevel loglevel = NORMAL;
|
||||||
DEBUG,
|
|
||||||
NORMAL
|
|
||||||
};
|
|
||||||
|
|
||||||
static enum loglevel loglevel = NORMAL;
|
|
||||||
|
|
||||||
/*******************
|
/*******************
|
||||||
* Data structures
|
* Data structures
|
||||||
* ****************/
|
* ****************/
|
||||||
struct section;
|
|
||||||
struct symbol;
|
|
||||||
struct rela;
|
|
||||||
|
|
||||||
enum status {
|
|
||||||
NEW,
|
|
||||||
CHANGED,
|
|
||||||
SAME
|
|
||||||
};
|
|
||||||
|
|
||||||
struct section {
|
|
||||||
struct list_head list;
|
|
||||||
struct section *twin;
|
|
||||||
GElf_Shdr sh;
|
|
||||||
Elf_Data *data;
|
|
||||||
char *name;
|
|
||||||
int index;
|
|
||||||
enum status status;
|
|
||||||
int include;
|
|
||||||
int ignore;
|
|
||||||
int grouped;
|
|
||||||
union {
|
|
||||||
struct { /* if (is_rela_section()) */
|
|
||||||
struct section *base;
|
|
||||||
struct list_head relas;
|
|
||||||
};
|
|
||||||
struct { /* else */
|
|
||||||
struct section *rela;
|
|
||||||
struct symbol *secsym, *sym;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct symbol {
|
|
||||||
struct list_head list;
|
|
||||||
struct symbol *twin;
|
|
||||||
struct section *sec;
|
|
||||||
GElf_Sym sym;
|
|
||||||
char *name;
|
|
||||||
int index;
|
|
||||||
unsigned char bind, type;
|
|
||||||
enum status status;
|
|
||||||
union {
|
|
||||||
int include; /* used in the patched elf */
|
|
||||||
int strip; /* used in the output elf */
|
|
||||||
};
|
|
||||||
int has_fentry_call;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rela {
|
|
||||||
struct list_head list;
|
|
||||||
GElf_Rela rela;
|
|
||||||
struct symbol *sym;
|
|
||||||
unsigned int type;
|
|
||||||
int addend;
|
|
||||||
int offset;
|
|
||||||
char *string;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct string {
|
|
||||||
struct list_head list;
|
|
||||||
char *name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct kpatch_elf {
|
|
||||||
Elf *elf;
|
|
||||||
struct list_head sections;
|
|
||||||
struct list_head symbols;
|
|
||||||
struct list_head strings;
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct special_section {
|
struct special_section {
|
||||||
char *name;
|
char *name;
|
||||||
int (*group_size)(struct kpatch_elf *kelf, int offset);
|
int (*group_size)(struct kpatch_elf *kelf, int offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*******************
|
|
||||||
* Helper functions
|
|
||||||
******************/
|
|
||||||
|
|
||||||
char *status_str(enum status status)
|
|
||||||
{
|
|
||||||
switch(status) {
|
|
||||||
case NEW:
|
|
||||||
return "NEW";
|
|
||||||
case CHANGED:
|
|
||||||
return "CHANGED";
|
|
||||||
case SAME:
|
|
||||||
return "SAME";
|
|
||||||
default:
|
|
||||||
ERROR("status_str");
|
|
||||||
}
|
|
||||||
/* never reached */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_rela_section(struct section *sec)
|
|
||||||
{
|
|
||||||
return (sec->sh.sh_type == SHT_RELA);
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_text_section(struct section *sec)
|
|
||||||
{
|
|
||||||
return (sec->sh.sh_type == SHT_PROGBITS &&
|
|
||||||
(sec->sh.sh_flags & SHF_EXECINSTR));
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_debug_section(struct section *sec)
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
if (is_rela_section(sec))
|
|
||||||
name = sec->base->name;
|
|
||||||
else
|
|
||||||
name = sec->name;
|
|
||||||
return !strncmp(name, ".debug_", 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct section *find_section_by_index(struct list_head *list, unsigned int index)
|
|
||||||
{
|
|
||||||
struct section *sec;
|
|
||||||
|
|
||||||
list_for_each_entry(sec, list, list)
|
|
||||||
if (sec->index == index)
|
|
||||||
return sec;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct section *find_section_by_name(struct list_head *list, const char *name)
|
|
||||||
{
|
|
||||||
struct section *sec;
|
|
||||||
|
|
||||||
list_for_each_entry(sec, list, list)
|
|
||||||
if (!strcmp(sec->name, name))
|
|
||||||
return sec;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct symbol *find_symbol_by_index(struct list_head *list, size_t index)
|
|
||||||
{
|
|
||||||
struct symbol *sym;
|
|
||||||
|
|
||||||
list_for_each_entry(sym, list, list)
|
|
||||||
if (sym->index == index)
|
|
||||||
return sym;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct symbol *find_symbol_by_name(struct list_head *list, const char *name)
|
|
||||||
{
|
|
||||||
struct symbol *sym;
|
|
||||||
|
|
||||||
list_for_each_entry(sym, list, list)
|
|
||||||
if (sym->name && !strcmp(sym->name, name))
|
|
||||||
return sym;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ALLOC_LINK(_new, _list) \
|
|
||||||
{ \
|
|
||||||
(_new) = malloc(sizeof(*(_new))); \
|
|
||||||
if (!(_new)) \
|
|
||||||
ERROR("malloc"); \
|
|
||||||
memset((_new), 0, sizeof(*(_new))); \
|
|
||||||
INIT_LIST_HEAD(&(_new)->list); \
|
|
||||||
list_add_tail(&(_new)->list, (_list)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* returns the offset of the string in the string table */
|
|
||||||
int offset_of_string(struct list_head *list, char *name)
|
|
||||||
{
|
|
||||||
struct string *string;
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
/* try to find string in the string list */
|
|
||||||
list_for_each_entry(string, list, list) {
|
|
||||||
if (!strcmp(string->name, name))
|
|
||||||
return index;
|
|
||||||
index += strlen(string->name) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate a new string */
|
|
||||||
ALLOC_LINK(string, list);
|
|
||||||
string->name = name;
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************
|
/*************
|
||||||
* Functions
|
* Functions
|
||||||
* **********/
|
* **********/
|
||||||
void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
|
|
||||||
{
|
|
||||||
int rela_nr, index = 0, skip = 0;
|
|
||||||
struct rela *rela;
|
|
||||||
unsigned int symndx;
|
|
||||||
|
|
||||||
/* find matching base (text/data) section */
|
|
||||||
sec->base = find_section_by_name(&kelf->sections, sec->name + 5);
|
|
||||||
if (!sec->base)
|
|
||||||
ERROR("can't find base section for rela section %s", sec->name);
|
|
||||||
|
|
||||||
/* create reverse link from base section to this rela section */
|
|
||||||
sec->base->rela = sec;
|
|
||||||
|
|
||||||
rela_nr = sec->sh.sh_size / sec->sh.sh_entsize;
|
|
||||||
|
|
||||||
log_debug("\n=== rela list for %s (%d entries) ===\n",
|
|
||||||
sec->base->name, rela_nr);
|
|
||||||
|
|
||||||
if (is_debug_section(sec)) {
|
|
||||||
log_debug("skipping rela listing for .debug_* section\n");
|
|
||||||
skip = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read and store the rela entries */
|
|
||||||
while (rela_nr--) {
|
|
||||||
ALLOC_LINK(rela, &sec->relas);
|
|
||||||
|
|
||||||
if (!gelf_getrela(sec->data, index, &rela->rela))
|
|
||||||
ERROR("gelf_getrela");
|
|
||||||
index++;
|
|
||||||
|
|
||||||
rela->type = GELF_R_TYPE(rela->rela.r_info);
|
|
||||||
rela->addend = rela->rela.r_addend;
|
|
||||||
rela->offset = rela->rela.r_offset;
|
|
||||||
symndx = GELF_R_SYM(rela->rela.r_info);
|
|
||||||
rela->sym = find_symbol_by_index(&kelf->symbols, symndx);
|
|
||||||
if (!rela->sym)
|
|
||||||
ERROR("could not find rela entry symbol\n");
|
|
||||||
if (rela->sym->sec &&
|
|
||||||
(rela->sym->sec->sh.sh_flags & SHF_STRINGS)) {
|
|
||||||
rela->string = rela->sym->sec->data->d_buf + rela->addend;
|
|
||||||
if (!rela->string)
|
|
||||||
ERROR("could not lookup rela string for %s+%d",
|
|
||||||
rela->sym->name, rela->addend);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skip)
|
|
||||||
continue;
|
|
||||||
log_debug("offset %d, type %d, %s %s %d", rela->offset,
|
|
||||||
rela->type, rela->sym->name,
|
|
||||||
(rela->addend < 0)?"-":"+", abs(rela->addend));
|
|
||||||
if (rela->string)
|
|
||||||
log_debug(" (string = %s)", rela->string);
|
|
||||||
log_debug("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_create_section_list(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
Elf_Scn *scn = NULL;
|
|
||||||
struct section *sec;
|
|
||||||
size_t shstrndx, sections_nr;
|
|
||||||
|
|
||||||
if (elf_getshdrnum(kelf->elf, §ions_nr))
|
|
||||||
ERROR("elf_getshdrnum");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* elf_getshdrnum() includes section index 0 but elf_nextscn
|
|
||||||
* doesn't return that section so subtract one.
|
|
||||||
*/
|
|
||||||
sections_nr--;
|
|
||||||
|
|
||||||
if (elf_getshdrstrndx(kelf->elf, &shstrndx))
|
|
||||||
ERROR("elf_getshdrstrndx");
|
|
||||||
|
|
||||||
log_debug("=== section list (%zu) ===\n", sections_nr);
|
|
||||||
|
|
||||||
while (sections_nr--) {
|
|
||||||
ALLOC_LINK(sec, &kelf->sections);
|
|
||||||
|
|
||||||
scn = elf_nextscn(kelf->elf, scn);
|
|
||||||
if (!scn)
|
|
||||||
ERROR("scn NULL");
|
|
||||||
|
|
||||||
if (!gelf_getshdr(scn, &sec->sh))
|
|
||||||
ERROR("gelf_getshdr");
|
|
||||||
|
|
||||||
sec->name = elf_strptr(kelf->elf, shstrndx, sec->sh.sh_name);
|
|
||||||
if (!sec->name)
|
|
||||||
ERROR("elf_strptr");
|
|
||||||
|
|
||||||
sec->data = elf_getdata(scn, NULL);
|
|
||||||
if (!sec->data)
|
|
||||||
ERROR("elf_getdata");
|
|
||||||
|
|
||||||
sec->index = elf_ndxscn(scn);
|
|
||||||
|
|
||||||
log_debug("ndx %02d, data %p, size %zu, name %s\n",
|
|
||||||
sec->index, sec->data->d_buf, sec->data->d_size,
|
|
||||||
sec->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sanity check, one more call to elf_nextscn() should return NULL */
|
|
||||||
if (elf_nextscn(kelf->elf, scn))
|
|
||||||
ERROR("expected NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_bundleable(struct symbol *sym)
|
|
||||||
{
|
|
||||||
if (sym->type == STT_FUNC &&
|
|
||||||
!strncmp(sym->sec->name, ".text.",6) &&
|
|
||||||
!strcmp(sym->sec->name + 6, sym->name))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (sym->type == STT_FUNC &&
|
|
||||||
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
|
||||||
!strcmp(sym->sec->name + 15, sym->name))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (sym->type == STT_OBJECT &&
|
|
||||||
!strncmp(sym->sec->name, ".data.",6) &&
|
|
||||||
!strcmp(sym->sec->name + 6, sym->name))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (sym->type == STT_OBJECT &&
|
|
||||||
!strncmp(sym->sec->name, ".rodata.",8) &&
|
|
||||||
!strcmp(sym->sec->name + 8, sym->name))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (sym->type == STT_OBJECT &&
|
|
||||||
!strncmp(sym->sec->name, ".bss.",5) &&
|
|
||||||
!strcmp(sym->sec->name + 5, sym->name))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_create_symbol_list(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct section *symtab;
|
|
||||||
struct symbol *sym;
|
|
||||||
int symbols_nr, index = 0;
|
|
||||||
|
|
||||||
symtab = find_section_by_name(&kelf->sections, ".symtab");
|
|
||||||
if (!symtab)
|
|
||||||
ERROR("missing symbol table");
|
|
||||||
|
|
||||||
symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
|
|
||||||
|
|
||||||
log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr);
|
|
||||||
|
|
||||||
while (symbols_nr--) {
|
|
||||||
ALLOC_LINK(sym, &kelf->symbols);
|
|
||||||
|
|
||||||
sym->index = index;
|
|
||||||
if (!gelf_getsym(symtab->data, index, &sym->sym))
|
|
||||||
ERROR("gelf_getsym");
|
|
||||||
index++;
|
|
||||||
|
|
||||||
sym->name = elf_strptr(kelf->elf, symtab->sh.sh_link,
|
|
||||||
sym->sym.st_name);
|
|
||||||
if (!sym->name)
|
|
||||||
ERROR("elf_strptr");
|
|
||||||
|
|
||||||
sym->type = GELF_ST_TYPE(sym->sym.st_info);
|
|
||||||
sym->bind = GELF_ST_BIND(sym->sym.st_info);
|
|
||||||
|
|
||||||
if (sym->sym.st_shndx > SHN_UNDEF &&
|
|
||||||
sym->sym.st_shndx < SHN_LORESERVE) {
|
|
||||||
sym->sec = find_section_by_index(&kelf->sections,
|
|
||||||
sym->sym.st_shndx);
|
|
||||||
if (!sym->sec)
|
|
||||||
ERROR("couldn't find section for symbol %s\n",
|
|
||||||
sym->name);
|
|
||||||
|
|
||||||
if (is_bundleable(sym)) {
|
|
||||||
if (sym->sym.st_value != 0)
|
|
||||||
ERROR("symbol %s at offset %lu within section %s, expected 0",
|
|
||||||
sym->name, sym->sym.st_value, sym->sec->name);
|
|
||||||
sym->sec->sym = sym;
|
|
||||||
} else if (sym->type == STT_SECTION) {
|
|
||||||
sym->sec->secsym = sym;
|
|
||||||
/* use the section name as the symbol name */
|
|
||||||
sym->name = sym->sec->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s",
|
|
||||||
sym->index, sym->type, sym->bind, sym->sym.st_shndx,
|
|
||||||
sym->name);
|
|
||||||
if (sym->sec)
|
|
||||||
log_debug(" -> %s", sym->sec->name);
|
|
||||||
log_debug("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check which functions have fentry calls; save this info for later use. */
|
|
||||||
static void kpatch_find_fentry_calls(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct symbol *sym;
|
|
||||||
struct rela *rela;
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
||||||
if (sym->type != STT_FUNC || !sym->sec->rela)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rela = list_first_entry(&sym->sec->rela->relas, struct rela,
|
|
||||||
list);
|
|
||||||
if (rela->type != R_X86_64_NONE ||
|
|
||||||
strcmp(rela->sym->name, "__fentry__"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sym->has_fentry_call = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct kpatch_elf *kpatch_elf_open(const char *name)
|
|
||||||
{
|
|
||||||
Elf *elf;
|
|
||||||
int fd;
|
|
||||||
struct kpatch_elf *kelf;
|
|
||||||
struct section *sec;
|
|
||||||
|
|
||||||
fd = open(name, O_RDONLY);
|
|
||||||
if (fd == -1)
|
|
||||||
ERROR("open");
|
|
||||||
|
|
||||||
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
|
||||||
if (!elf)
|
|
||||||
ERROR("elf_begin");
|
|
||||||
|
|
||||||
kelf = malloc(sizeof(*kelf));
|
|
||||||
if (!kelf)
|
|
||||||
ERROR("malloc");
|
|
||||||
memset(kelf, 0, sizeof(*kelf));
|
|
||||||
INIT_LIST_HEAD(&kelf->sections);
|
|
||||||
INIT_LIST_HEAD(&kelf->symbols);
|
|
||||||
INIT_LIST_HEAD(&kelf->strings);
|
|
||||||
|
|
||||||
/* read and store section, symbol entries from file */
|
|
||||||
kelf->elf = elf;
|
|
||||||
kelf->fd = fd;
|
|
||||||
kpatch_create_section_list(kelf);
|
|
||||||
kpatch_create_symbol_list(kelf);
|
|
||||||
|
|
||||||
/* for each rela section, read and store the rela entries */
|
|
||||||
list_for_each_entry(sec, &kelf->sections, list) {
|
|
||||||
if (!is_rela_section(sec))
|
|
||||||
continue;
|
|
||||||
INIT_LIST_HEAD(&sec->relas);
|
|
||||||
kpatch_create_rela_list(kelf, sec);
|
|
||||||
}
|
|
||||||
|
|
||||||
kpatch_find_fentry_calls(kelf);
|
|
||||||
return kelf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function detects whether the given symbol is a "special" static local
|
* This function detects whether the given symbol is a "special" static local
|
||||||
@ -957,7 +498,6 @@ void kpatch_check_program_headers(Elf *elf)
|
|||||||
DIFF_FATAL("ELF contains program header");
|
DIFF_FATAL("ELF contains program header");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void kpatch_mark_grouped_sections(struct kpatch_elf *kelf)
|
void kpatch_mark_grouped_sections(struct kpatch_elf *kelf)
|
||||||
{
|
{
|
||||||
struct section *groupsec, *sec;
|
struct section *groupsec, *sec;
|
||||||
@ -1448,54 +988,6 @@ void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
|
|||||||
log_debug("\n");
|
log_debug("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void kpatch_dump_kelf(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct section *sec;
|
|
||||||
struct symbol *sym;
|
|
||||||
struct rela *rela;
|
|
||||||
|
|
||||||
if (loglevel > DEBUG)
|
|
||||||
return;
|
|
||||||
|
|
||||||
printf("\n=== Sections ===\n");
|
|
||||||
list_for_each_entry(sec, &kelf->sections, list) {
|
|
||||||
printf("%02d %s (%s)", sec->index, sec->name, status_str(sec->status));
|
|
||||||
if (is_rela_section(sec)) {
|
|
||||||
printf(", base-> %s\n", sec->base->name);
|
|
||||||
/* skip .debug_* sections */
|
|
||||||
if (is_debug_section(sec))
|
|
||||||
goto next;
|
|
||||||
printf("rela section expansion\n");
|
|
||||||
list_for_each_entry(rela, &sec->relas, list) {
|
|
||||||
printf("sym %d, offset %d, type %d, %s %s %d\n",
|
|
||||||
rela->sym->index, rela->offset,
|
|
||||||
rela->type, rela->sym->name,
|
|
||||||
(rela->addend < 0)?"-":"+",
|
|
||||||
abs(rela->addend));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (sec->sym)
|
|
||||||
printf(", sym-> %s", sec->sym->name);
|
|
||||||
if (sec->secsym)
|
|
||||||
printf(", secsym-> %s", sec->secsym->name);
|
|
||||||
if (sec->rela)
|
|
||||||
printf(", rela-> %s", sec->rela->name);
|
|
||||||
}
|
|
||||||
next:
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n=== Symbols ===\n");
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
||||||
printf("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)",
|
|
||||||
sym->index, sym->type, sym->bind, sym->sym.st_shndx,
|
|
||||||
sym->name, status_str(sym->status));
|
|
||||||
if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT))
|
|
||||||
printf(" -> %s", sym->sec->name);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void kpatch_check_fentry_calls(struct kpatch_elf *kelf)
|
static void kpatch_check_fentry_calls(struct kpatch_elf *kelf)
|
||||||
{
|
{
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
@ -1754,26 +1246,6 @@ void kpatch_migrate_symbols(struct list_head *src,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_null_sym(struct symbol *sym)
|
|
||||||
{
|
|
||||||
return !strlen(sym->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_file_sym(struct symbol *sym)
|
|
||||||
{
|
|
||||||
return sym->type == STT_FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_local_func_sym(struct symbol *sym)
|
|
||||||
{
|
|
||||||
return sym->bind == STB_LOCAL && sym->type == STT_FUNC;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_local_sym(struct symbol *sym)
|
|
||||||
{
|
|
||||||
return sym->bind == STB_LOCAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_migrate_included_elements(struct kpatch_elf *kelf, struct kpatch_elf **kelfout)
|
void kpatch_migrate_included_elements(struct kpatch_elf *kelf, struct kpatch_elf **kelfout)
|
||||||
{
|
{
|
||||||
struct section *sec, *safesec;
|
struct section *sec, *safesec;
|
||||||
@ -1856,7 +1328,6 @@ void kpatch_reindex_elements(struct kpatch_elf *kelf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int bug_table_group_size(struct kpatch_elf *kelf, int offset)
|
int bug_table_group_size(struct kpatch_elf *kelf, int offset)
|
||||||
{
|
{
|
||||||
static int size = 0;
|
static int size = 0;
|
||||||
@ -2289,214 +1760,6 @@ void kpatch_process_special_sections(struct kpatch_elf *kelf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_strtab(char *buf, size_t size)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
if (buf[i] == 0)
|
|
||||||
printf("\\0");
|
|
||||||
else
|
|
||||||
printf("%c",buf[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_create_shstrtab(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct section *shstrtab, *sec;
|
|
||||||
size_t size, offset, len;
|
|
||||||
char *buf;
|
|
||||||
|
|
||||||
shstrtab = find_section_by_name(&kelf->sections, ".shstrtab");
|
|
||||||
if (!shstrtab)
|
|
||||||
ERROR("find_section_by_name");
|
|
||||||
|
|
||||||
/* determine size of string table */
|
|
||||||
size = 1; /* for initial NULL terminator */
|
|
||||||
list_for_each_entry(sec, &kelf->sections, list)
|
|
||||||
size += strlen(sec->name) + 1; /* include NULL terminator */
|
|
||||||
|
|
||||||
/* allocate data buffer */
|
|
||||||
buf = malloc(size);
|
|
||||||
if (!buf)
|
|
||||||
ERROR("malloc");
|
|
||||||
memset(buf, 0, size);
|
|
||||||
|
|
||||||
/* populate string table and link with section header */
|
|
||||||
offset = 1;
|
|
||||||
list_for_each_entry(sec, &kelf->sections, list) {
|
|
||||||
len = strlen(sec->name) + 1;
|
|
||||||
sec->sh.sh_name = offset;
|
|
||||||
memcpy(buf + offset, sec->name, len);
|
|
||||||
offset += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset != size)
|
|
||||||
ERROR("shstrtab size mismatch");
|
|
||||||
|
|
||||||
shstrtab->data->d_buf = buf;
|
|
||||||
shstrtab->data->d_size = size;
|
|
||||||
|
|
||||||
if (loglevel <= DEBUG) {
|
|
||||||
printf("shstrtab: ");
|
|
||||||
print_strtab(buf, size);
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
list_for_each_entry(sec, &kelf->sections, list)
|
|
||||||
printf("%s @ shstrtab offset %d\n",
|
|
||||||
sec->name, sec->sh.sh_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_create_strtab(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct section *strtab;
|
|
||||||
struct symbol *sym;
|
|
||||||
size_t size = 0, offset = 0, len;
|
|
||||||
char *buf;
|
|
||||||
|
|
||||||
strtab = find_section_by_name(&kelf->sections, ".strtab");
|
|
||||||
if (!strtab)
|
|
||||||
ERROR("find_section_by_name");
|
|
||||||
|
|
||||||
/* determine size of string table */
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
||||||
if (sym->type == STT_SECTION)
|
|
||||||
continue;
|
|
||||||
size += strlen(sym->name) + 1; /* include NULL terminator */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate data buffer */
|
|
||||||
buf = malloc(size);
|
|
||||||
if (!buf)
|
|
||||||
ERROR("malloc");
|
|
||||||
memset(buf, 0, size);
|
|
||||||
|
|
||||||
/* populate string table and link with section header */
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
||||||
if (sym->type == STT_SECTION) {
|
|
||||||
sym->sym.st_name = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
len = strlen(sym->name) + 1;
|
|
||||||
sym->sym.st_name = offset;
|
|
||||||
memcpy(buf + offset, sym->name, len);
|
|
||||||
offset += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset != size)
|
|
||||||
ERROR("shstrtab size mismatch");
|
|
||||||
|
|
||||||
strtab->data->d_buf = buf;
|
|
||||||
strtab->data->d_size = size;
|
|
||||||
|
|
||||||
if (loglevel <= DEBUG) {
|
|
||||||
printf("strtab: ");
|
|
||||||
print_strtab(buf, size);
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list)
|
|
||||||
printf("%s @ strtab offset %d\n",
|
|
||||||
sym->name, sym->sym.st_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_create_symtab(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct section *symtab;
|
|
||||||
struct symbol *sym;
|
|
||||||
char *buf;
|
|
||||||
size_t size;
|
|
||||||
int nr = 0, offset = 0, nr_local = 0;
|
|
||||||
|
|
||||||
symtab = find_section_by_name(&kelf->sections, ".symtab");
|
|
||||||
if (!symtab)
|
|
||||||
ERROR("find_section_by_name");
|
|
||||||
|
|
||||||
/* count symbols */
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list)
|
|
||||||
nr++;
|
|
||||||
|
|
||||||
/* create new symtab buffer */
|
|
||||||
size = nr * symtab->sh.sh_entsize;
|
|
||||||
buf = malloc(size);
|
|
||||||
if (!buf)
|
|
||||||
ERROR("malloc");
|
|
||||||
memset(buf, 0, size);
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
list_for_each_entry(sym, &kelf->symbols, list) {
|
|
||||||
memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize);
|
|
||||||
offset += symtab->sh.sh_entsize;
|
|
||||||
|
|
||||||
if (is_local_sym(sym))
|
|
||||||
nr_local++;
|
|
||||||
}
|
|
||||||
|
|
||||||
symtab->data->d_buf = buf;
|
|
||||||
symtab->data->d_size = size;
|
|
||||||
|
|
||||||
/* update symtab section header */
|
|
||||||
symtab->sh.sh_link = find_section_by_name(&kelf->sections, ".strtab")->index;
|
|
||||||
symtab->sh.sh_info = nr_local;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct section *create_section_pair(struct kpatch_elf *kelf, char *name,
|
|
||||||
int entsize, int nr)
|
|
||||||
{
|
|
||||||
char *relaname;
|
|
||||||
struct section *sec, *relasec;
|
|
||||||
int size = entsize * nr;
|
|
||||||
|
|
||||||
relaname = malloc(strlen(name) + strlen(".rela") + 1);
|
|
||||||
if (!relaname)
|
|
||||||
ERROR("malloc");
|
|
||||||
strcpy(relaname, ".rela");
|
|
||||||
strcat(relaname, name);
|
|
||||||
|
|
||||||
/* allocate text section resources */
|
|
||||||
ALLOC_LINK(sec, &kelf->sections);
|
|
||||||
sec->name = name;
|
|
||||||
|
|
||||||
/* set data */
|
|
||||||
sec->data = malloc(sizeof(*sec->data));
|
|
||||||
if (!sec->data)
|
|
||||||
ERROR("malloc");
|
|
||||||
sec->data->d_buf = malloc(size);
|
|
||||||
if (!sec->data->d_buf)
|
|
||||||
ERROR("malloc");
|
|
||||||
sec->data->d_size = size;
|
|
||||||
sec->data->d_type = ELF_T_BYTE;
|
|
||||||
|
|
||||||
/* set section header */
|
|
||||||
sec->sh.sh_type = SHT_PROGBITS;
|
|
||||||
sec->sh.sh_entsize = entsize;
|
|
||||||
sec->sh.sh_addralign = 8;
|
|
||||||
sec->sh.sh_flags = SHF_ALLOC;
|
|
||||||
sec->sh.sh_size = size;
|
|
||||||
|
|
||||||
/* allocate rela section resources */
|
|
||||||
ALLOC_LINK(relasec, &kelf->sections);
|
|
||||||
relasec->name = relaname;
|
|
||||||
relasec->base = sec;
|
|
||||||
INIT_LIST_HEAD(&relasec->relas);
|
|
||||||
|
|
||||||
/* set data, buffers generated by kpatch_rebuild_rela_section_data() */
|
|
||||||
relasec->data = malloc(sizeof(*relasec->data));
|
|
||||||
if (!relasec->data)
|
|
||||||
ERROR("malloc");
|
|
||||||
|
|
||||||
/* set section header */
|
|
||||||
relasec->sh.sh_type = SHT_RELA;
|
|
||||||
relasec->sh.sh_entsize = sizeof(GElf_Rela);
|
|
||||||
relasec->sh.sh_addralign = 8;
|
|
||||||
|
|
||||||
/* set text rela section pointer */
|
|
||||||
sec->rela = relasec;
|
|
||||||
|
|
||||||
return sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
void kpatch_create_patches_sections(struct kpatch_elf *kelf,
|
||||||
struct lookup_table *table, char *hint,
|
struct lookup_table *table, char *hint,
|
||||||
char *objname)
|
char *objname)
|
||||||
@ -2982,76 +2245,6 @@ void kpatch_rebuild_rela_section_data(struct section *sec)
|
|||||||
ERROR("size mismatch in rebuilt rela section");
|
ERROR("size mismatch in rebuilt rela section");
|
||||||
}
|
}
|
||||||
|
|
||||||
void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
struct section *sec;
|
|
||||||
Elf *elfout;
|
|
||||||
GElf_Ehdr eh, ehout;
|
|
||||||
Elf_Scn *scn;
|
|
||||||
Elf_Data *data;
|
|
||||||
GElf_Shdr sh;
|
|
||||||
|
|
||||||
/* TODO make this argv */
|
|
||||||
fd = creat(outfile, 0777);
|
|
||||||
if (fd == -1)
|
|
||||||
ERROR("creat");
|
|
||||||
|
|
||||||
elfout = elf_begin(fd, ELF_C_WRITE, NULL);
|
|
||||||
if (!elfout)
|
|
||||||
ERROR("elf_begin");
|
|
||||||
|
|
||||||
if (!gelf_newehdr(elfout, gelf_getclass(kelf->elf)))
|
|
||||||
ERROR("gelf_newehdr");
|
|
||||||
|
|
||||||
if (!gelf_getehdr(elfout, &ehout))
|
|
||||||
ERROR("gelf_getehdr");
|
|
||||||
|
|
||||||
if (!gelf_getehdr(elf, &eh))
|
|
||||||
ERROR("gelf_getehdr");
|
|
||||||
|
|
||||||
memset(&ehout, 0, sizeof(ehout));
|
|
||||||
ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA];
|
|
||||||
ehout.e_machine = eh.e_machine;
|
|
||||||
ehout.e_type = eh.e_type;
|
|
||||||
ehout.e_version = EV_CURRENT;
|
|
||||||
ehout.e_shstrndx = find_section_by_name(&kelf->sections, ".shstrtab")->index;
|
|
||||||
|
|
||||||
/* add changed sections */
|
|
||||||
list_for_each_entry(sec, &kelf->sections, list) {
|
|
||||||
scn = elf_newscn(elfout);
|
|
||||||
if (!scn)
|
|
||||||
ERROR("elf_newscn");
|
|
||||||
|
|
||||||
data = elf_newdata(scn);
|
|
||||||
if (!data)
|
|
||||||
ERROR("elf_newdata");
|
|
||||||
|
|
||||||
if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY))
|
|
||||||
ERROR("elf_flagdata");
|
|
||||||
|
|
||||||
data->d_type = sec->data->d_type;
|
|
||||||
data->d_buf = sec->data->d_buf;
|
|
||||||
data->d_size = sec->data->d_size;
|
|
||||||
|
|
||||||
if(!gelf_getshdr(scn, &sh))
|
|
||||||
ERROR("gelf_getshdr");
|
|
||||||
|
|
||||||
sh = sec->sh;
|
|
||||||
|
|
||||||
if (!gelf_update_shdr(scn, &sh))
|
|
||||||
ERROR("gelf_update_shdr");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gelf_update_ehdr(elfout, &ehout))
|
|
||||||
ERROR("gelf_update_ehdr");
|
|
||||||
|
|
||||||
if (elf_update(elfout, ELF_C_WRITE) < 0) {
|
|
||||||
printf("%s\n",elf_errmsg(-1));
|
|
||||||
ERROR("elf_update");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct arguments {
|
struct arguments {
|
||||||
char *args[4];
|
char *args[4];
|
||||||
int debug;
|
int debug;
|
||||||
@ -3094,47 +2287,6 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state)
|
|||||||
|
|
||||||
static struct argp argp = { options, parse_opt, args_doc, 0 };
|
static struct argp argp = { options, parse_opt, args_doc, 0 };
|
||||||
|
|
||||||
/*
|
|
||||||
* While this is a one-shot program without a lot of proper cleanup in case
|
|
||||||
* of an error, this function serves a debugging purpose: to break down and
|
|
||||||
* zero data structures we shouldn't be accessing anymore. This should
|
|
||||||
* help cause an immediate and obvious issue when a logic error leads to
|
|
||||||
* accessing data that is not intended to be accessed past a particular point.
|
|
||||||
*/
|
|
||||||
void kpatch_elf_teardown(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
struct section *sec, *safesec;
|
|
||||||
struct symbol *sym, *safesym;
|
|
||||||
struct rela *rela, *saferela;
|
|
||||||
|
|
||||||
list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
|
|
||||||
if (is_rela_section(sec)) {
|
|
||||||
list_for_each_entry_safe(rela, saferela, &sec->relas, list) {
|
|
||||||
memset(rela, 0, sizeof(*rela));
|
|
||||||
free(rela);
|
|
||||||
}
|
|
||||||
memset(sec, 0, sizeof(*sec));
|
|
||||||
free(sec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
|
|
||||||
memset(sym, 0, sizeof(*sym));
|
|
||||||
free(sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&kelf->sections);
|
|
||||||
INIT_LIST_HEAD(&kelf->symbols);
|
|
||||||
}
|
|
||||||
|
|
||||||
void kpatch_elf_free(struct kpatch_elf *kelf)
|
|
||||||
{
|
|
||||||
elf_end(kelf->elf);
|
|
||||||
close(kelf->fd);
|
|
||||||
memset(kelf, 0, sizeof(*kelf));
|
|
||||||
free(kelf);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct kpatch_elf *kelf_base, *kelf_patched, *kelf_out;
|
struct kpatch_elf *kelf_base, *kelf_patched, *kelf_out;
|
||||||
|
783
kpatch-build/kpatch-elf.c
Normal file
783
kpatch-build/kpatch-elf.c
Normal file
@ -0,0 +1,783 @@
|
|||||||
|
/*
|
||||||
|
* kpatch-elf.c
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file provides a common api to create, inspect, and manipulate
|
||||||
|
* kpatch_elf objects.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <error.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "kpatch-elf.h"
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
* Helper functions
|
||||||
|
******************/
|
||||||
|
|
||||||
|
char *status_str(enum status status)
|
||||||
|
{
|
||||||
|
switch(status) {
|
||||||
|
case NEW:
|
||||||
|
return "NEW";
|
||||||
|
case CHANGED:
|
||||||
|
return "CHANGED";
|
||||||
|
case SAME:
|
||||||
|
return "SAME";
|
||||||
|
default:
|
||||||
|
ERROR("status_str");
|
||||||
|
}
|
||||||
|
/* never reached */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_rela_section(struct section *sec)
|
||||||
|
{
|
||||||
|
return (sec->sh.sh_type == SHT_RELA);
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_text_section(struct section *sec)
|
||||||
|
{
|
||||||
|
return (sec->sh.sh_type == SHT_PROGBITS &&
|
||||||
|
(sec->sh.sh_flags & SHF_EXECINSTR));
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_debug_section(struct section *sec)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
if (is_rela_section(sec))
|
||||||
|
name = sec->base->name;
|
||||||
|
else
|
||||||
|
name = sec->name;
|
||||||
|
return !strncmp(name, ".debug_", 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct section *find_section_by_index(struct list_head *list, unsigned int index)
|
||||||
|
{
|
||||||
|
struct section *sec;
|
||||||
|
|
||||||
|
list_for_each_entry(sec, list, list)
|
||||||
|
if (sec->index == index)
|
||||||
|
return sec;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct section *find_section_by_name(struct list_head *list, const char *name)
|
||||||
|
{
|
||||||
|
struct section *sec;
|
||||||
|
|
||||||
|
list_for_each_entry(sec, list, list)
|
||||||
|
if (!strcmp(sec->name, name))
|
||||||
|
return sec;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct symbol *find_symbol_by_index(struct list_head *list, size_t index)
|
||||||
|
{
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
|
list_for_each_entry(sym, list, list)
|
||||||
|
if (sym->index == index)
|
||||||
|
return sym;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct symbol *find_symbol_by_name(struct list_head *list, const char *name)
|
||||||
|
{
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
|
list_for_each_entry(sym, list, list)
|
||||||
|
if (sym->name && !strcmp(sym->name, name))
|
||||||
|
return sym;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns the offset of the string in the string table */
|
||||||
|
int offset_of_string(struct list_head *list, char *name)
|
||||||
|
{
|
||||||
|
struct string *string;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
/* try to find string in the string list */
|
||||||
|
list_for_each_entry(string, list, list) {
|
||||||
|
if (!strcmp(string->name, name))
|
||||||
|
return index;
|
||||||
|
index += strlen(string->name) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate a new string */
|
||||||
|
ALLOC_LINK(string, list);
|
||||||
|
string->name = name;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec)
|
||||||
|
{
|
||||||
|
int rela_nr, index = 0, skip = 0;
|
||||||
|
struct rela *rela;
|
||||||
|
unsigned int symndx;
|
||||||
|
|
||||||
|
/* find matching base (text/data) section */
|
||||||
|
sec->base = find_section_by_name(&kelf->sections, sec->name + 5);
|
||||||
|
if (!sec->base)
|
||||||
|
ERROR("can't find base section for rela section %s", sec->name);
|
||||||
|
|
||||||
|
/* create reverse link from base section to this rela section */
|
||||||
|
sec->base->rela = sec;
|
||||||
|
|
||||||
|
rela_nr = sec->sh.sh_size / sec->sh.sh_entsize;
|
||||||
|
|
||||||
|
log_debug("\n=== rela list for %s (%d entries) ===\n",
|
||||||
|
sec->base->name, rela_nr);
|
||||||
|
|
||||||
|
if (is_debug_section(sec)) {
|
||||||
|
log_debug("skipping rela listing for .debug_* section\n");
|
||||||
|
skip = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read and store the rela entries */
|
||||||
|
while (rela_nr--) {
|
||||||
|
ALLOC_LINK(rela, &sec->relas);
|
||||||
|
|
||||||
|
if (!gelf_getrela(sec->data, index, &rela->rela))
|
||||||
|
ERROR("gelf_getrela");
|
||||||
|
index++;
|
||||||
|
|
||||||
|
rela->type = GELF_R_TYPE(rela->rela.r_info);
|
||||||
|
rela->addend = rela->rela.r_addend;
|
||||||
|
rela->offset = rela->rela.r_offset;
|
||||||
|
symndx = GELF_R_SYM(rela->rela.r_info);
|
||||||
|
rela->sym = find_symbol_by_index(&kelf->symbols, symndx);
|
||||||
|
if (!rela->sym)
|
||||||
|
ERROR("could not find rela entry symbol\n");
|
||||||
|
if (rela->sym->sec &&
|
||||||
|
(rela->sym->sec->sh.sh_flags & SHF_STRINGS)) {
|
||||||
|
rela->string = rela->sym->sec->data->d_buf + rela->addend;
|
||||||
|
if (!rela->string)
|
||||||
|
ERROR("could not lookup rela string for %s+%d",
|
||||||
|
rela->sym->name, rela->addend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip)
|
||||||
|
continue;
|
||||||
|
log_debug("offset %d, type %d, %s %s %d", rela->offset,
|
||||||
|
rela->type, rela->sym->name,
|
||||||
|
(rela->addend < 0)?"-":"+", abs(rela->addend));
|
||||||
|
if (rela->string)
|
||||||
|
log_debug(" (string = %s)", rela->string);
|
||||||
|
log_debug("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_create_section_list(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
Elf_Scn *scn = NULL;
|
||||||
|
struct section *sec;
|
||||||
|
size_t shstrndx, sections_nr;
|
||||||
|
|
||||||
|
if (elf_getshdrnum(kelf->elf, §ions_nr))
|
||||||
|
ERROR("elf_getshdrnum");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* elf_getshdrnum() includes section index 0 but elf_nextscn
|
||||||
|
* doesn't return that section so subtract one.
|
||||||
|
*/
|
||||||
|
sections_nr--;
|
||||||
|
|
||||||
|
if (elf_getshdrstrndx(kelf->elf, &shstrndx))
|
||||||
|
ERROR("elf_getshdrstrndx");
|
||||||
|
|
||||||
|
log_debug("=== section list (%zu) ===\n", sections_nr);
|
||||||
|
|
||||||
|
while (sections_nr--) {
|
||||||
|
ALLOC_LINK(sec, &kelf->sections);
|
||||||
|
|
||||||
|
scn = elf_nextscn(kelf->elf, scn);
|
||||||
|
if (!scn)
|
||||||
|
ERROR("scn NULL");
|
||||||
|
|
||||||
|
if (!gelf_getshdr(scn, &sec->sh))
|
||||||
|
ERROR("gelf_getshdr");
|
||||||
|
|
||||||
|
sec->name = elf_strptr(kelf->elf, shstrndx, sec->sh.sh_name);
|
||||||
|
if (!sec->name)
|
||||||
|
ERROR("elf_strptr");
|
||||||
|
|
||||||
|
sec->data = elf_getdata(scn, NULL);
|
||||||
|
if (!sec->data)
|
||||||
|
ERROR("elf_getdata");
|
||||||
|
|
||||||
|
sec->index = elf_ndxscn(scn);
|
||||||
|
|
||||||
|
log_debug("ndx %02d, data %p, size %zu, name %s\n",
|
||||||
|
sec->index, sec->data->d_buf, sec->data->d_size,
|
||||||
|
sec->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sanity check, one more call to elf_nextscn() should return NULL */
|
||||||
|
if (elf_nextscn(kelf->elf, scn))
|
||||||
|
ERROR("expected NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_bundleable(struct symbol *sym)
|
||||||
|
{
|
||||||
|
if (sym->type == STT_FUNC &&
|
||||||
|
!strncmp(sym->sec->name, ".text.",6) &&
|
||||||
|
!strcmp(sym->sec->name + 6, sym->name))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (sym->type == STT_FUNC &&
|
||||||
|
!strncmp(sym->sec->name, ".text.unlikely.",15) &&
|
||||||
|
!strcmp(sym->sec->name + 15, sym->name))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (sym->type == STT_OBJECT &&
|
||||||
|
!strncmp(sym->sec->name, ".data.",6) &&
|
||||||
|
!strcmp(sym->sec->name + 6, sym->name))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (sym->type == STT_OBJECT &&
|
||||||
|
!strncmp(sym->sec->name, ".rodata.",8) &&
|
||||||
|
!strcmp(sym->sec->name + 8, sym->name))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (sym->type == STT_OBJECT &&
|
||||||
|
!strncmp(sym->sec->name, ".bss.",5) &&
|
||||||
|
!strcmp(sym->sec->name + 5, sym->name))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_create_symbol_list(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct section *symtab;
|
||||||
|
struct symbol *sym;
|
||||||
|
int symbols_nr, index = 0;
|
||||||
|
|
||||||
|
symtab = find_section_by_name(&kelf->sections, ".symtab");
|
||||||
|
if (!symtab)
|
||||||
|
ERROR("missing symbol table");
|
||||||
|
|
||||||
|
symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
|
||||||
|
|
||||||
|
log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr);
|
||||||
|
|
||||||
|
while (symbols_nr--) {
|
||||||
|
ALLOC_LINK(sym, &kelf->symbols);
|
||||||
|
|
||||||
|
sym->index = index;
|
||||||
|
if (!gelf_getsym(symtab->data, index, &sym->sym))
|
||||||
|
ERROR("gelf_getsym");
|
||||||
|
index++;
|
||||||
|
|
||||||
|
sym->name = elf_strptr(kelf->elf, symtab->sh.sh_link,
|
||||||
|
sym->sym.st_name);
|
||||||
|
if (!sym->name)
|
||||||
|
ERROR("elf_strptr");
|
||||||
|
|
||||||
|
sym->type = GELF_ST_TYPE(sym->sym.st_info);
|
||||||
|
sym->bind = GELF_ST_BIND(sym->sym.st_info);
|
||||||
|
|
||||||
|
if (sym->sym.st_shndx > SHN_UNDEF &&
|
||||||
|
sym->sym.st_shndx < SHN_LORESERVE) {
|
||||||
|
sym->sec = find_section_by_index(&kelf->sections,
|
||||||
|
sym->sym.st_shndx);
|
||||||
|
if (!sym->sec)
|
||||||
|
ERROR("couldn't find section for symbol %s\n",
|
||||||
|
sym->name);
|
||||||
|
|
||||||
|
if (is_bundleable(sym)) {
|
||||||
|
if (sym->sym.st_value != 0)
|
||||||
|
ERROR("symbol %s at offset %lu within section %s, expected 0",
|
||||||
|
sym->name, sym->sym.st_value, sym->sec->name);
|
||||||
|
sym->sec->sym = sym;
|
||||||
|
} else if (sym->type == STT_SECTION) {
|
||||||
|
sym->sec->secsym = sym;
|
||||||
|
/* use the section name as the symbol name */
|
||||||
|
sym->name = sym->sec->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s",
|
||||||
|
sym->index, sym->type, sym->bind, sym->sym.st_shndx,
|
||||||
|
sym->name);
|
||||||
|
if (sym->sec)
|
||||||
|
log_debug(" -> %s", sym->sec->name);
|
||||||
|
log_debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check which functions have fentry calls; save this info for later use. */
|
||||||
|
static void kpatch_find_fentry_calls(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct symbol *sym;
|
||||||
|
struct rela *rela;
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||||
|
if (sym->type != STT_FUNC || !sym->sec->rela)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rela = list_first_entry(&sym->sec->rela->relas, struct rela,
|
||||||
|
list);
|
||||||
|
if (rela->type != R_X86_64_NONE ||
|
||||||
|
strcmp(rela->sym->name, "__fentry__"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sym->has_fentry_call = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kpatch_elf *kpatch_elf_open(const char *name)
|
||||||
|
{
|
||||||
|
Elf *elf;
|
||||||
|
int fd;
|
||||||
|
struct kpatch_elf *kelf;
|
||||||
|
struct section *sec;
|
||||||
|
|
||||||
|
fd = open(name, O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
ERROR("open");
|
||||||
|
|
||||||
|
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
||||||
|
if (!elf)
|
||||||
|
ERROR("elf_begin");
|
||||||
|
|
||||||
|
kelf = malloc(sizeof(*kelf));
|
||||||
|
if (!kelf)
|
||||||
|
ERROR("malloc");
|
||||||
|
memset(kelf, 0, sizeof(*kelf));
|
||||||
|
INIT_LIST_HEAD(&kelf->sections);
|
||||||
|
INIT_LIST_HEAD(&kelf->symbols);
|
||||||
|
INIT_LIST_HEAD(&kelf->strings);
|
||||||
|
|
||||||
|
/* read and store section, symbol entries from file */
|
||||||
|
kelf->elf = elf;
|
||||||
|
kelf->fd = fd;
|
||||||
|
kpatch_create_section_list(kelf);
|
||||||
|
kpatch_create_symbol_list(kelf);
|
||||||
|
|
||||||
|
/* for each rela section, read and store the rela entries */
|
||||||
|
list_for_each_entry(sec, &kelf->sections, list) {
|
||||||
|
if (!is_rela_section(sec))
|
||||||
|
continue;
|
||||||
|
INIT_LIST_HEAD(&sec->relas);
|
||||||
|
kpatch_create_rela_list(kelf, sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
kpatch_find_fentry_calls(kelf);
|
||||||
|
return kelf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_dump_kelf(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct section *sec;
|
||||||
|
struct symbol *sym;
|
||||||
|
struct rela *rela;
|
||||||
|
|
||||||
|
if (loglevel > DEBUG)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("\n=== Sections ===\n");
|
||||||
|
list_for_each_entry(sec, &kelf->sections, list) {
|
||||||
|
printf("%02d %s (%s)", sec->index, sec->name, status_str(sec->status));
|
||||||
|
if (is_rela_section(sec)) {
|
||||||
|
printf(", base-> %s\n", sec->base->name);
|
||||||
|
/* skip .debug_* sections */
|
||||||
|
if (is_debug_section(sec))
|
||||||
|
goto next;
|
||||||
|
printf("rela section expansion\n");
|
||||||
|
list_for_each_entry(rela, &sec->relas, list) {
|
||||||
|
printf("sym %d, offset %d, type %d, %s %s %d\n",
|
||||||
|
rela->sym->index, rela->offset,
|
||||||
|
rela->type, rela->sym->name,
|
||||||
|
(rela->addend < 0)?"-":"+",
|
||||||
|
abs(rela->addend));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sec->sym)
|
||||||
|
printf(", sym-> %s", sec->sym->name);
|
||||||
|
if (sec->secsym)
|
||||||
|
printf(", secsym-> %s", sec->secsym->name);
|
||||||
|
if (sec->rela)
|
||||||
|
printf(", rela-> %s", sec->rela->name);
|
||||||
|
}
|
||||||
|
next:
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n=== Symbols ===\n");
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||||
|
printf("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)",
|
||||||
|
sym->index, sym->type, sym->bind, sym->sym.st_shndx,
|
||||||
|
sym->name, status_str(sym->status));
|
||||||
|
if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT))
|
||||||
|
printf(" -> %s", sym->sec->name);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_null_sym(struct symbol *sym)
|
||||||
|
{
|
||||||
|
return !strlen(sym->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_file_sym(struct symbol *sym)
|
||||||
|
{
|
||||||
|
return sym->type == STT_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_local_func_sym(struct symbol *sym)
|
||||||
|
{
|
||||||
|
return sym->bind == STB_LOCAL && sym->type == STT_FUNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_local_sym(struct symbol *sym)
|
||||||
|
{
|
||||||
|
return sym->bind == STB_LOCAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_strtab(char *buf, size_t size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
if (buf[i] == 0)
|
||||||
|
printf("\\0");
|
||||||
|
else
|
||||||
|
printf("%c",buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_create_shstrtab(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct section *shstrtab, *sec;
|
||||||
|
size_t size, offset, len;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
shstrtab = find_section_by_name(&kelf->sections, ".shstrtab");
|
||||||
|
if (!shstrtab)
|
||||||
|
ERROR("find_section_by_name");
|
||||||
|
|
||||||
|
/* determine size of string table */
|
||||||
|
size = 1; /* for initial NULL terminator */
|
||||||
|
list_for_each_entry(sec, &kelf->sections, list)
|
||||||
|
size += strlen(sec->name) + 1; /* include NULL terminator */
|
||||||
|
|
||||||
|
/* allocate data buffer */
|
||||||
|
buf = malloc(size);
|
||||||
|
if (!buf)
|
||||||
|
ERROR("malloc");
|
||||||
|
memset(buf, 0, size);
|
||||||
|
|
||||||
|
/* populate string table and link with section header */
|
||||||
|
offset = 1;
|
||||||
|
list_for_each_entry(sec, &kelf->sections, list) {
|
||||||
|
len = strlen(sec->name) + 1;
|
||||||
|
sec->sh.sh_name = offset;
|
||||||
|
memcpy(buf + offset, sec->name, len);
|
||||||
|
offset += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset != size)
|
||||||
|
ERROR("shstrtab size mismatch");
|
||||||
|
|
||||||
|
shstrtab->data->d_buf = buf;
|
||||||
|
shstrtab->data->d_size = size;
|
||||||
|
|
||||||
|
if (loglevel <= DEBUG) {
|
||||||
|
printf("shstrtab: ");
|
||||||
|
print_strtab(buf, size);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
list_for_each_entry(sec, &kelf->sections, list)
|
||||||
|
printf("%s @ shstrtab offset %d\n",
|
||||||
|
sec->name, sec->sh.sh_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_create_strtab(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct section *strtab;
|
||||||
|
struct symbol *sym;
|
||||||
|
size_t size = 0, offset = 0, len;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
strtab = find_section_by_name(&kelf->sections, ".strtab");
|
||||||
|
if (!strtab)
|
||||||
|
ERROR("find_section_by_name");
|
||||||
|
|
||||||
|
/* determine size of string table */
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||||
|
if (sym->type == STT_SECTION)
|
||||||
|
continue;
|
||||||
|
size += strlen(sym->name) + 1; /* include NULL terminator */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate data buffer */
|
||||||
|
buf = malloc(size);
|
||||||
|
if (!buf)
|
||||||
|
ERROR("malloc");
|
||||||
|
memset(buf, 0, size);
|
||||||
|
|
||||||
|
/* populate string table and link with section header */
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||||
|
if (sym->type == STT_SECTION) {
|
||||||
|
sym->sym.st_name = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
len = strlen(sym->name) + 1;
|
||||||
|
sym->sym.st_name = offset;
|
||||||
|
memcpy(buf + offset, sym->name, len);
|
||||||
|
offset += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset != size)
|
||||||
|
ERROR("shstrtab size mismatch");
|
||||||
|
|
||||||
|
strtab->data->d_buf = buf;
|
||||||
|
strtab->data->d_size = size;
|
||||||
|
|
||||||
|
if (loglevel <= DEBUG) {
|
||||||
|
printf("strtab: ");
|
||||||
|
print_strtab(buf, size);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list)
|
||||||
|
printf("%s @ strtab offset %d\n",
|
||||||
|
sym->name, sym->sym.st_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_create_symtab(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct section *symtab;
|
||||||
|
struct symbol *sym;
|
||||||
|
char *buf;
|
||||||
|
size_t size;
|
||||||
|
int nr = 0, offset = 0, nr_local = 0;
|
||||||
|
|
||||||
|
symtab = find_section_by_name(&kelf->sections, ".symtab");
|
||||||
|
if (!symtab)
|
||||||
|
ERROR("find_section_by_name");
|
||||||
|
|
||||||
|
/* count symbols */
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list)
|
||||||
|
nr++;
|
||||||
|
|
||||||
|
/* create new symtab buffer */
|
||||||
|
size = nr * symtab->sh.sh_entsize;
|
||||||
|
buf = malloc(size);
|
||||||
|
if (!buf)
|
||||||
|
ERROR("malloc");
|
||||||
|
memset(buf, 0, size);
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
list_for_each_entry(sym, &kelf->symbols, list) {
|
||||||
|
memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize);
|
||||||
|
offset += symtab->sh.sh_entsize;
|
||||||
|
|
||||||
|
if (is_local_sym(sym))
|
||||||
|
nr_local++;
|
||||||
|
}
|
||||||
|
|
||||||
|
symtab->data->d_buf = buf;
|
||||||
|
symtab->data->d_size = size;
|
||||||
|
|
||||||
|
/* update symtab section header */
|
||||||
|
symtab->sh.sh_link = find_section_by_name(&kelf->sections, ".strtab")->index;
|
||||||
|
symtab->sh.sh_info = nr_local;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct section *create_section_pair(struct kpatch_elf *kelf, char *name,
|
||||||
|
int entsize, int nr)
|
||||||
|
{
|
||||||
|
char *relaname;
|
||||||
|
struct section *sec, *relasec;
|
||||||
|
int size = entsize * nr;
|
||||||
|
|
||||||
|
relaname = malloc(strlen(name) + strlen(".rela") + 1);
|
||||||
|
if (!relaname)
|
||||||
|
ERROR("malloc");
|
||||||
|
strcpy(relaname, ".rela");
|
||||||
|
strcat(relaname, name);
|
||||||
|
|
||||||
|
/* allocate text section resources */
|
||||||
|
ALLOC_LINK(sec, &kelf->sections);
|
||||||
|
sec->name = name;
|
||||||
|
|
||||||
|
/* set data */
|
||||||
|
sec->data = malloc(sizeof(*sec->data));
|
||||||
|
if (!sec->data)
|
||||||
|
ERROR("malloc");
|
||||||
|
sec->data->d_buf = malloc(size);
|
||||||
|
if (!sec->data->d_buf)
|
||||||
|
ERROR("malloc");
|
||||||
|
sec->data->d_size = size;
|
||||||
|
sec->data->d_type = ELF_T_BYTE;
|
||||||
|
|
||||||
|
/* set section header */
|
||||||
|
sec->sh.sh_type = SHT_PROGBITS;
|
||||||
|
sec->sh.sh_entsize = entsize;
|
||||||
|
sec->sh.sh_addralign = 8;
|
||||||
|
sec->sh.sh_flags = SHF_ALLOC;
|
||||||
|
sec->sh.sh_size = size;
|
||||||
|
|
||||||
|
/* allocate rela section resources */
|
||||||
|
ALLOC_LINK(relasec, &kelf->sections);
|
||||||
|
relasec->name = relaname;
|
||||||
|
relasec->base = sec;
|
||||||
|
INIT_LIST_HEAD(&relasec->relas);
|
||||||
|
|
||||||
|
/* set data, buffers generated by kpatch_rebuild_rela_section_data() */
|
||||||
|
relasec->data = malloc(sizeof(*relasec->data));
|
||||||
|
if (!relasec->data)
|
||||||
|
ERROR("malloc");
|
||||||
|
|
||||||
|
/* set section header */
|
||||||
|
relasec->sh.sh_type = SHT_RELA;
|
||||||
|
relasec->sh.sh_entsize = sizeof(GElf_Rela);
|
||||||
|
relasec->sh.sh_addralign = 8;
|
||||||
|
|
||||||
|
/* set text rela section pointer */
|
||||||
|
sec->rela = relasec;
|
||||||
|
|
||||||
|
return sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct section *sec;
|
||||||
|
Elf *elfout;
|
||||||
|
GElf_Ehdr eh, ehout;
|
||||||
|
Elf_Scn *scn;
|
||||||
|
Elf_Data *data;
|
||||||
|
GElf_Shdr sh;
|
||||||
|
|
||||||
|
/* TODO make this argv */
|
||||||
|
fd = creat(outfile, 0777);
|
||||||
|
if (fd == -1)
|
||||||
|
ERROR("creat");
|
||||||
|
|
||||||
|
elfout = elf_begin(fd, ELF_C_WRITE, NULL);
|
||||||
|
if (!elfout)
|
||||||
|
ERROR("elf_begin");
|
||||||
|
|
||||||
|
if (!gelf_newehdr(elfout, gelf_getclass(kelf->elf)))
|
||||||
|
ERROR("gelf_newehdr");
|
||||||
|
|
||||||
|
if (!gelf_getehdr(elfout, &ehout))
|
||||||
|
ERROR("gelf_getehdr");
|
||||||
|
|
||||||
|
if (!gelf_getehdr(elf, &eh))
|
||||||
|
ERROR("gelf_getehdr");
|
||||||
|
|
||||||
|
memset(&ehout, 0, sizeof(ehout));
|
||||||
|
ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA];
|
||||||
|
ehout.e_machine = eh.e_machine;
|
||||||
|
ehout.e_type = eh.e_type;
|
||||||
|
ehout.e_version = EV_CURRENT;
|
||||||
|
ehout.e_shstrndx = find_section_by_name(&kelf->sections, ".shstrtab")->index;
|
||||||
|
|
||||||
|
/* add changed sections */
|
||||||
|
list_for_each_entry(sec, &kelf->sections, list) {
|
||||||
|
scn = elf_newscn(elfout);
|
||||||
|
if (!scn)
|
||||||
|
ERROR("elf_newscn");
|
||||||
|
|
||||||
|
data = elf_newdata(scn);
|
||||||
|
if (!data)
|
||||||
|
ERROR("elf_newdata");
|
||||||
|
|
||||||
|
if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY))
|
||||||
|
ERROR("elf_flagdata");
|
||||||
|
|
||||||
|
data->d_type = sec->data->d_type;
|
||||||
|
data->d_buf = sec->data->d_buf;
|
||||||
|
data->d_size = sec->data->d_size;
|
||||||
|
|
||||||
|
if(!gelf_getshdr(scn, &sh))
|
||||||
|
ERROR("gelf_getshdr");
|
||||||
|
|
||||||
|
sh = sec->sh;
|
||||||
|
|
||||||
|
if (!gelf_update_shdr(scn, &sh))
|
||||||
|
ERROR("gelf_update_shdr");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gelf_update_ehdr(elfout, &ehout))
|
||||||
|
ERROR("gelf_update_ehdr");
|
||||||
|
|
||||||
|
if (elf_update(elfout, ELF_C_WRITE) < 0) {
|
||||||
|
printf("%s\n",elf_errmsg(-1));
|
||||||
|
ERROR("elf_update");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* While this is a one-shot program without a lot of proper cleanup in case
|
||||||
|
* of an error, this function serves a debugging purpose: to break down and
|
||||||
|
* zero data structures we shouldn't be accessing anymore. This should
|
||||||
|
* help cause an immediate and obvious issue when a logic error leads to
|
||||||
|
* accessing data that is not intended to be accessed past a particular point.
|
||||||
|
*/
|
||||||
|
void kpatch_elf_teardown(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
struct section *sec, *safesec;
|
||||||
|
struct symbol *sym, *safesym;
|
||||||
|
struct rela *rela, *saferela;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
|
||||||
|
if (is_rela_section(sec)) {
|
||||||
|
list_for_each_entry_safe(rela, saferela, &sec->relas, list) {
|
||||||
|
memset(rela, 0, sizeof(*rela));
|
||||||
|
free(rela);
|
||||||
|
}
|
||||||
|
memset(sec, 0, sizeof(*sec));
|
||||||
|
free(sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
|
||||||
|
memset(sym, 0, sizeof(*sym));
|
||||||
|
free(sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&kelf->sections);
|
||||||
|
INIT_LIST_HEAD(&kelf->symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kpatch_elf_free(struct kpatch_elf *kelf)
|
||||||
|
{
|
||||||
|
elf_end(kelf->elf);
|
||||||
|
close(kelf->fd);
|
||||||
|
memset(kelf, 0, sizeof(*kelf));
|
||||||
|
free(kelf);
|
||||||
|
}
|
150
kpatch-build/kpatch-elf.h
Normal file
150
kpatch-build/kpatch-elf.h
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* kpatch-elf.h
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _KPATCH_ELF_H_
|
||||||
|
#define _KPATCH_ELF_H_
|
||||||
|
|
||||||
|
#include <gelf.h>
|
||||||
|
#include "list.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
* Data structures
|
||||||
|
* ****************/
|
||||||
|
struct section;
|
||||||
|
struct symbol;
|
||||||
|
struct rela;
|
||||||
|
|
||||||
|
enum status {
|
||||||
|
NEW,
|
||||||
|
CHANGED,
|
||||||
|
SAME
|
||||||
|
};
|
||||||
|
|
||||||
|
struct section {
|
||||||
|
struct list_head list;
|
||||||
|
struct section *twin;
|
||||||
|
GElf_Shdr sh;
|
||||||
|
Elf_Data *data;
|
||||||
|
char *name;
|
||||||
|
int index;
|
||||||
|
enum status status;
|
||||||
|
int include;
|
||||||
|
int ignore;
|
||||||
|
int grouped;
|
||||||
|
union {
|
||||||
|
struct { /* if (is_rela_section()) */
|
||||||
|
struct section *base;
|
||||||
|
struct list_head relas;
|
||||||
|
};
|
||||||
|
struct { /* else */
|
||||||
|
struct section *rela;
|
||||||
|
struct symbol *secsym, *sym;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbol {
|
||||||
|
struct list_head list;
|
||||||
|
struct symbol *twin;
|
||||||
|
struct section *sec;
|
||||||
|
GElf_Sym sym;
|
||||||
|
char *name;
|
||||||
|
int index;
|
||||||
|
unsigned char bind, type;
|
||||||
|
enum status status;
|
||||||
|
union {
|
||||||
|
int include; /* used in the patched elf */
|
||||||
|
int strip; /* used in the output elf */
|
||||||
|
};
|
||||||
|
int has_fentry_call;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rela {
|
||||||
|
struct list_head list;
|
||||||
|
GElf_Rela rela;
|
||||||
|
struct symbol *sym;
|
||||||
|
unsigned int type;
|
||||||
|
int addend;
|
||||||
|
int offset;
|
||||||
|
char *string;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string {
|
||||||
|
struct list_head list;
|
||||||
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kpatch_elf {
|
||||||
|
Elf *elf;
|
||||||
|
struct list_head sections;
|
||||||
|
struct list_head symbols;
|
||||||
|
struct list_head strings;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
* Helper functions
|
||||||
|
******************/
|
||||||
|
char *status_str(enum status status);
|
||||||
|
int is_rela_section(struct section *sec);
|
||||||
|
int is_text_section(struct section *sec);
|
||||||
|
int is_debug_section(struct section *sec);
|
||||||
|
|
||||||
|
struct section *find_section_by_index(struct list_head *list, unsigned int index);
|
||||||
|
struct section *find_section_by_name(struct list_head *list, const char *name);
|
||||||
|
struct symbol *find_symbol_by_index(struct list_head *list, size_t index);
|
||||||
|
struct symbol *find_symbol_by_name(struct list_head *list, const char *name);
|
||||||
|
|
||||||
|
#define ALLOC_LINK(_new, _list) \
|
||||||
|
{ \
|
||||||
|
(_new) = malloc(sizeof(*(_new))); \
|
||||||
|
if (!(_new)) \
|
||||||
|
ERROR("malloc"); \
|
||||||
|
memset((_new), 0, sizeof(*(_new))); \
|
||||||
|
INIT_LIST_HEAD(&(_new)->list); \
|
||||||
|
list_add_tail(&(_new)->list, (_list)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset_of_string(struct list_head *list, char *name);
|
||||||
|
|
||||||
|
/*************
|
||||||
|
* Functions
|
||||||
|
* **********/
|
||||||
|
void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec);
|
||||||
|
void kpatch_create_section_list(struct kpatch_elf *kelf);
|
||||||
|
void kpatch_create_symbol_list(struct kpatch_elf *kelf);
|
||||||
|
struct kpatch_elf *kpatch_elf_open(const char *name);
|
||||||
|
void kpatch_dump_kelf(struct kpatch_elf *kelf);
|
||||||
|
|
||||||
|
int is_null_sym(struct symbol *sym);
|
||||||
|
int is_file_sym(struct symbol *sym);
|
||||||
|
int is_local_func_sym(struct symbol *sym);
|
||||||
|
int is_local_sym(struct symbol *sym);
|
||||||
|
|
||||||
|
void print_strtab(char *buf, size_t size);
|
||||||
|
void kpatch_create_shstrtab(struct kpatch_elf *kelf);
|
||||||
|
void kpatch_create_strtab(struct kpatch_elf *kelf);
|
||||||
|
void kpatch_create_symtab(struct kpatch_elf *kelf);
|
||||||
|
struct section *create_section_pair(struct kpatch_elf *kelf, char *name,
|
||||||
|
int entsize, int nr);
|
||||||
|
void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile);
|
||||||
|
void kpatch_elf_teardown(struct kpatch_elf *kelf);
|
||||||
|
void kpatch_elf_free(struct kpatch_elf *kelf);
|
||||||
|
#endif /* _KPATCH_ELF_H_ */
|
24
kpatch-build/log.h
Normal file
24
kpatch-build/log.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _LOG_H_
|
||||||
|
#define _LOG_H_
|
||||||
|
|
||||||
|
/* Files that include log.h must define loglevel and childobj */
|
||||||
|
extern enum loglevel loglevel;
|
||||||
|
extern char *childobj;
|
||||||
|
|
||||||
|
#define ERROR(format, ...) \
|
||||||
|
error(1, 0, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__)
|
||||||
|
#define log_normal(format, ...) log(NORMAL, "%s: " format, childobj, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define log(level, format, ...) \
|
||||||
|
({ \
|
||||||
|
if (loglevel <= (level)) \
|
||||||
|
printf(format, ##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
|
||||||
|
enum loglevel {
|
||||||
|
DEBUG,
|
||||||
|
NORMAL
|
||||||
|
};
|
||||||
|
#endif /* _LOG_H_ */
|
Loading…
Reference in New Issue
Block a user