create-diff-object
+prelink
*.o
*.d
CFLAGS += -Iinsn -Wall -g
LDFLAGS = -lelf
-TARGETS = create-diff-object
-OBJS = create-diff-object.o lookup.o insn/insn.o insn/inat.o
-SOURCES = create-diff-object.c lookup.c insn/insn.c insn/inat.c
+TARGETS = create-diff-object prelink
+CREATE_DIFF_OBJECT_OBJS = create-diff-object.o lookup.o insn/insn.o insn/inat.o common.o
+PRELINK_OBJS = prelink.o lookup.o insn/insn.o insn/inat.o common.o
+SOURCES = create-diff-object.c prelink.c lookup.c insn/insn.c insn/inat.c common.c
all: $(TARGETS)
%.o : %.c
$(CC) -MMD -MP $(CFLAGS) -c -o $@ $<
-create-diff-object: $(OBJS)
+create-diff-object: $(CREATE_DIFF_OBJECT_OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
+
+prelink: $(PRELINK_OBJS)
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
clean:
- $(RM) $(TARGETS) $(OBJS) *.d insn/*.d
+ $(RM) $(TARGETS) $(CREATE_DIFF_OBJECT_OBJS) $(PRELINK_OBJS) *.d insn/*.d
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <gelf.h>
+
+#include "list.h"
+#include "lookup.h"
+#include "asm/insn.h"
+#include "common.h"
+
+int is_rela_section(struct section *sec)
+{
+ return (sec->sh.sh_type == SHT_RELA);
+}
+
+int is_local_sym(struct symbol *sym)
+{
+ return sym->bind == STB_LOCAL;
+}
+
+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;
+}
+
+static void xsplice_create_section_list(struct xsplice_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;
+}
+
+static void xsplice_create_symbol_list(struct xsplice_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");
+ }
+
+}
+
+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_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);
+}
+
+/* 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 rela_insn(struct section *sec, struct rela *rela, struct insn *insn)
+{
+ unsigned long insn_addr, start, end, rela_addr;
+
+ start = (unsigned long)sec->base->data->d_buf;
+ end = start + sec->base->sh.sh_size;
+ rela_addr = start + rela->offset;
+ for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
+ insn_init(insn, (void *)insn_addr, 1);
+ insn_get_length(insn);
+ if (!insn->length)
+ ERROR("can't decode instruction in section %s at offset 0x%lx",
+ sec->base->name, insn_addr);
+ if (rela_addr >= insn_addr &&
+ rela_addr < insn_addr + insn->length)
+ return;
+ }
+}
+
+static void xsplice_create_rela_list(struct xsplice_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)) {
+ /* XXX This differs from upstream. Send a pull request. */
+ rela->string = rela->sym->sec->data->d_buf +
+ rela->sym->sym.st_value + rela->addend;
+
+ /*
+ * XXX This is a bit of a hack. Investigate with
+ * upstream.
+ */
+ /*
+ * If the relocation is relative to %rip, the addend
+ * includes the length of the instruction, so offset by
+ * the length of the instruction to get the actual
+ * string (if the relocation is to a .text section).
+ * If the relocation is to a debug frame, ignore the
+ * since it encodes the line number.
+ */
+ if (rela->type == R_X86_64_PC32) {
+ if (!strncmp(sec->base->name, ".text", 5)) {
+ struct insn insn;
+ rela_insn(sec, rela, &insn);
+ rela->string += (long)insn.next_byte -
+ (long)sec->base->data->d_buf -
+ rela->offset;
+ } else {
+ rela->string -= 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");
+ }
+}
+
+struct xsplice_elf *xsplice_elf_open(const char *name)
+{
+ Elf *elf;
+ int fd;
+ struct xsplice_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;
+ xsplice_create_section_list(kelf);
+ xsplice_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);
+ xsplice_create_rela_list(kelf, sec);
+ }
+
+ return kelf;
+}
+
+/*
+ * 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 xsplice_elf_teardown(struct xsplice_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 xsplice_elf_free(struct xsplice_elf *kelf)
+{
+ elf_end(kelf->elf);
+ close(kelf->fd);
+ memset(kelf, 0, sizeof(*kelf));
+ free(kelf);
+}
+
+void xsplice_write_output_elf(struct xsplice_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");
+ }
+}
+
+void xsplice_dump_kelf(struct xsplice_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 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 xsplice_create_shstrtab(struct xsplice_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 xsplice_create_strtab(struct xsplice_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 xsplice_create_symtab(struct xsplice_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;
+}
+
+void xsplice_rebuild_rela_section_data(struct section *sec)
+{
+ struct rela *rela;
+ int nr = 0, index = 0, size;
+ GElf_Rela *relas;
+
+ list_for_each_entry(rela, &sec->relas, list)
+ nr++;
+
+ size = nr * sizeof(*relas);
+ relas = malloc(size);
+ if (!relas)
+ ERROR("malloc");
+
+ sec->data->d_buf = relas;
+ sec->data->d_size = size;
+ /* d_type remains ELF_T_RELA */
+
+ sec->sh.sh_size = size;
+
+ list_for_each_entry(rela, &sec->relas, list) {
+ relas[index].r_offset = rela->offset;
+ relas[index].r_addend = rela->addend;
+ relas[index].r_info = GELF_R_INFO(rela->sym->index, rela->type);
+ index++;
+ }
+
+ /* sanity check, index should equal nr */
+ if (index != nr)
+ ERROR("size mismatch in rebuilt rela section");
+}
--- /dev/null
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <error.h>
+
+static char *childobj;
+
+#define ERROR(format, ...) \
+ error(1, 0, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+#define DIFF_FATAL(format, ...) \
+({ \
+ fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
+ 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__); \
+})
+
+#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)); \
+}
+
+enum loglevel {
+ DEBUG,
+ NORMAL
+};
+
+static enum loglevel loglevel = NORMAL;
+
+/*******************
+ * 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 */
+ };
+};
+
+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 xsplice_elf {
+ Elf *elf;
+ struct list_head sections;
+ struct list_head symbols;
+ struct list_head strings;
+ int fd;
+};
+
+#define PATCH_INSN_SIZE 5
+
+struct xsplice_patch_func {
+ unsigned long new_addr;
+ unsigned long new_size;
+ unsigned long old_addr;
+ unsigned long old_size;
+ char *name;
+ unsigned char undo[8];
+};
+
+struct special_section {
+ char *name;
+ int (*group_size)(struct xsplice_elf *kelf, int offset);
+};
+
+struct xsplice_elf *xsplice_elf_open(const char *name);
+void xsplice_elf_free(struct xsplice_elf *kelf);
+void xsplice_elf_teardown(struct xsplice_elf *kelf);
+void xsplice_write_output_elf(struct xsplice_elf *kelf,
+ Elf *elf, char *outfile);
+void xsplice_dump_kelf(struct xsplice_elf *kelf);
+void xsplice_create_symtab(struct xsplice_elf *kelf);
+void xsplice_create_strtab(struct xsplice_elf *kelf);
+void xsplice_create_shstrtab(struct xsplice_elf *kelf);
+void xsplice_rebuild_rela_section_data(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);
+
+int is_text_section(struct section *sec);
+int is_debug_section(struct section *sec);
+int is_rela_section(struct section *sec);
+int is_local_sym(struct symbol *sym);
+
+void rela_insn(struct section *sec, struct rela *rela, struct insn *insn);
+
+int offset_of_string(struct list_head *list, char *name);
+
+char *status_str(enum status status);
+
+#endif /* _COMMON_H_ */
#include "list.h"
#include "lookup.h"
#include "asm/insn.h"
-
-#define ERROR(format, ...) \
- error(1, 0, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
-
-#define DIFF_FATAL(format, ...) \
-({ \
- fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
- 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__); \
-})
-
-static char *childobj;
-
-enum loglevel {
- DEBUG,
- NORMAL
-};
-
-static enum loglevel loglevel = NORMAL;
-
-/*******************
- * 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 */
- };
-};
-
-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 xsplice_elf {
- Elf *elf;
- struct list_head sections;
- struct list_head symbols;
- struct list_head strings;
- int fd;
-};
-
-#define PATCH_INSN_SIZE 5
-
-struct xsplice_patch_func {
- unsigned long new_addr;
- unsigned long new_size;
- unsigned long old_addr;
- unsigned long old_size;
- char *name;
- unsigned char undo[8];
-};
-
-struct special_section {
- char *name;
- int (*group_size)(struct xsplice_elf *kelf, int offset);
-};
-
-/*******************
- * Helper functions
- ******************/
-
-static 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;
-}
-
-static int is_rela_section(struct section *sec)
-{
- return (sec->sh.sh_type == SHT_RELA);
-}
-
-static int is_text_section(struct section *sec)
-{
- return (sec->sh.sh_type == SHT_PROGBITS &&
- (sec->sh.sh_flags & SHF_EXECINSTR));
-}
-
-static 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);
-}
-
-static 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;
-}
-
-static 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;
-}
-
-static 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;
-}
-
-static 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 */
-static 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;
-}
-
-static void rela_insn(struct section *sec, struct rela *rela,
- struct insn *insn)
-{
- unsigned long insn_addr, start, end, rela_addr;
-
- start = (unsigned long)sec->base->data->d_buf;
- end = start + sec->base->sh.sh_size;
- rela_addr = start + rela->offset;
- for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
- insn_init(insn, (void *)insn_addr, 1);
- insn_get_length(insn);
- if (!insn->length)
- ERROR("can't decode instruction in section %s at offset 0x%lx",
- sec->base->name, insn_addr);
- if (rela_addr >= insn_addr &&
- rela_addr < insn_addr + insn->length)
- return;
- }
-}
-
-/*************
- * Functions
- * **********/
-static void xsplice_create_rela_list(struct xsplice_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)) {
- /* XXX This differs from upstream. Send a pull request. */
- rela->string = rela->sym->sec->data->d_buf +
- rela->sym->sym.st_value + rela->addend;
-
- /*
- * XXX This is a bit of a hack. Investigate with
- * upstream.
- */
- /*
- * If the relocation is relative to %rip, the addend
- * includes the length of the instruction, so offset by
- * the length of the instruction to get the actual
- * string (if the relocation is to a .text section).
- * If the relocation is to a debug frame, ignore the
- * since it encodes the line number.
- */
- if (rela->type == R_X86_64_PC32) {
- if (!strncmp(sec->base->name, ".text", 5)) {
- struct insn insn;
- rela_insn(sec, rela, &insn);
- rela->string += (long)insn.next_byte -
- (long)sec->base->data->d_buf -
- rela->offset;
- } else {
- rela->string -= 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");
- }
-}
-
-static void xsplice_create_section_list(struct xsplice_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;
-}
-
-static void xsplice_create_symbol_list(struct xsplice_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");
- }
-
-}
-
-static struct xsplice_elf *xsplice_elf_open(const char *name)
-{
- Elf *elf;
- int fd;
- struct xsplice_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;
- xsplice_create_section_list(kelf);
- xsplice_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);
- xsplice_create_rela_list(kelf, sec);
- }
-
- return kelf;
-}
+#include "common.h"
static void xsplice_compare_elf_headers(Elf *elf1, Elf *elf2)
{
xsplice_compare_symbols(&kelf->symbols);
}
-/*
- * 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.
- */
-static void xsplice_elf_teardown(struct xsplice_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);
-}
-
-static void xsplice_elf_free(struct xsplice_elf *kelf)
-{
- elf_end(kelf->elf);
- close(kelf->fd);
- memset(kelf, 0, sizeof(*kelf));
- free(kelf);
-}
-
static void xsplice_mark_ignored_sections_same(struct xsplice_elf *kelf)
{
struct section *sec;
}
}
-static void xsplice_dump_kelf(struct xsplice_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 xsplice_verify_patchability(struct xsplice_elf *kelf)
{
struct section *sec;
return sym->bind == STB_LOCAL && sym->type == STT_FUNC;
}
-static int is_local_sym(struct symbol *sym)
-{
- return sym->bind == STB_LOCAL;
-}
-
static void xsplice_reorder_symbols(struct xsplice_elf *kelf)
{
LIST_HEAD(symbols);
}
}
-static void xsplice_rebuild_rela_section_data(struct section *sec)
-{
- struct rela *rela;
- int nr = 0, index = 0, size;
- GElf_Rela *relas;
-
- list_for_each_entry(rela, &sec->relas, list)
- nr++;
-
- size = nr * sizeof(*relas);
- relas = malloc(size);
- if (!relas)
- ERROR("malloc");
-
- sec->data->d_buf = relas;
- sec->data->d_size = size;
- /* d_type remains ELF_T_RELA */
-
- sec->sh.sh_size = size;
-
- list_for_each_entry(rela, &sec->relas, list) {
- relas[index].r_offset = rela->offset;
- relas[index].r_addend = rela->addend;
- relas[index].r_info = GELF_R_INFO(rela->sym->index, rela->type);
- index++;
- }
-
- /* sanity check, index should equal nr */
- if (index != nr)
- ERROR("size mismatch in rebuilt rela section");
-}
-
-static 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]);
- }
-}
-
-static void xsplice_create_shstrtab(struct xsplice_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);
- }
-}
-
-static void xsplice_create_strtab(struct xsplice_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);
- }
-}
-
-static void xsplice_create_symtab(struct xsplice_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;
-}
-
-static void xsplice_write_output_elf(struct xsplice_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 {
char *args[4];
int debug;
--- /dev/null
+/*
+ * Copyright (C) 2015 Ross Lagerwall <ross.lagerwall@citrix.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This tool takes a generated patch and a xen-syms file and fills in the
+ * undefined symbols (i.e. it does static partial linking).
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <argp.h>
+#include <error.h>
+#include <unistd.h>
+#include <gelf.h>
+
+#include "list.h"
+#include "lookup.h"
+#include "asm/insn.h"
+#include "common.h"
+
+/* Resolve symbols using xen-syms */
+void xsplice_resolve_symbols(struct xsplice_elf *kelf,
+ struct lookup_table *table)
+{
+ struct symbol *sym;
+ struct lookup_result result;
+ char *curfile = NULL;
+
+ list_for_each_entry(sym, &kelf->symbols, list) {
+ /* ignore NULL symbol */
+ if (!strlen(sym->name))
+ continue;
+
+ if (sym->type == STT_FILE) {
+ curfile = sym->name;
+ log_debug("Local file is %s\n", curfile);
+ }
+
+ if (sym->sec)
+ continue;
+ if (sym->sym.st_shndx != SHN_UNDEF)
+ continue;
+
+ if (sym->bind == STB_LOCAL) {
+ if (lookup_local_symbol(table, sym->name,
+ curfile, &result))
+ ERROR("lookup_local_symbol %s (%s)",
+ sym->name, curfile);
+ } else {
+ if (lookup_global_symbol(table, sym->name,
+ &result))
+ ERROR("lookup_global_symbol %s",
+ sym->name);
+ }
+ log_debug("lookup for %s @ 0x%016lx len %lu\n",
+ sym->name, result.value, result.size);
+ sym->sym.st_value = result.value;
+ sym->sym.st_shndx = SHN_ABS;
+ }
+}
+
+struct arguments {
+ char *args[3];
+ int debug;
+};
+
+static char args_doc[] = "original.o resolved.o xen-syms";
+
+static struct argp_option options[] = {
+ {"debug", 'd', 0, 0, "Show debug output" },
+ { 0 }
+};
+
+static error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* Get the input argument from argp_parse, which we
+ know is a pointer to our arguments structure. */
+ struct arguments *arguments = state->input;
+
+ switch (key)
+ {
+ case 'd':
+ arguments->debug = 1;
+ break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num >= 3)
+ /* Too many arguments. */
+ argp_usage (state);
+ arguments->args[state->arg_num] = arg;
+ break;
+ case ARGP_KEY_END:
+ if (state->arg_num < 3)
+ /* Not enough arguments. */
+ argp_usage (state);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, 0 };
+
+int main(int argc, char *argv[])
+{
+ struct xsplice_elf *kelf;
+ struct arguments arguments;
+ struct lookup_table *lookup;
+ struct section *sec, *symtab;
+
+ arguments.debug = 0;
+ argp_parse (&argp, argc, argv, 0, 0, &arguments);
+ if (arguments.debug)
+ loglevel = DEBUG;
+
+ elf_version(EV_CURRENT);
+
+ childobj = basename(arguments.args[0]);
+
+ log_debug("Open elf\n");
+ kelf = xsplice_elf_open(arguments.args[0]);
+
+ /* create symbol lookup table */
+ log_debug("Lookup xen-syms\n");
+ lookup = lookup_open(arguments.args[2]);
+
+ log_debug("Resolve symbols\n");
+ xsplice_resolve_symbols(kelf, lookup);
+
+ /*
+ * Update rela section headers and rebuild the rela section data
+ * buffers from the relas lists.
+ */
+ symtab = find_section_by_name(&kelf->sections, ".symtab");
+ list_for_each_entry(sec, &kelf->sections, list) {
+ if (!is_rela_section(sec))
+ continue;
+ sec->sh.sh_link = symtab->index;
+ sec->sh.sh_info = sec->base->index;
+ log_debug("Rebuild rela section data for %s\n", sec->name);
+ xsplice_rebuild_rela_section_data(sec);
+ }
+
+ log_debug("Create shstrtab\n");
+ xsplice_create_shstrtab(kelf);
+ log_debug("Create strtab\n");
+ xsplice_create_strtab(kelf);
+ log_debug("Create symtab\n");
+ xsplice_create_symtab(kelf);
+
+ log_debug("Dump elf status\n");
+ xsplice_dump_kelf(kelf);
+
+ log_debug("Write out elf\n");
+ xsplice_write_output_elf(kelf, kelf->elf, arguments.args[1]);
+
+ log_debug("Elf teardown\n");
+ xsplice_elf_teardown(kelf);
+ log_debug("Elf free\n");
+ xsplice_elf_free(kelf);
+
+ return 0;
+}
fi
echo "Creating patch module..."
- ld -r -o "${PATCHNAME}.xsplice" $(find output -type f -name "*.o") || die
+ ld -r -o output.o $(find output -type f -name "*.o") || die
+ "${SCRIPTDIR}"/prelink $debugopt output.o "${PATCHNAME}.xsplice" "$XENSYMS" &>> "${OUTPUT}/prelink.log" || die
}
usage() {