]> xenbits.xensource.com Git - people/wipawel/livepatch-build-tools/commitdiff
Add prelink tool
authorRoss Lagerwall <ross.lagerwall@citrix.com>
Tue, 20 Oct 2015 15:51:37 +0000 (16:51 +0100)
committerRoss Lagerwall <ross.lagerwall@citrix.com>
Tue, 20 Oct 2015 15:51:37 +0000 (16:51 +0100)
To simplify matters, prelink against a xen-syms file at compile time to
avoid having to resolve symbols at runtime. Create a prelink tool to do
this. Split out the elf loading and saving code of create-diff-object.c
into a common file which is shared by create-diff-object and prelink.

.gitignore
Makefile
common.c [new file with mode: 0644]
common.h [new file with mode: 0644]
create-diff-object.c
prelink.c [new file with mode: 0644]
xsplice-build

index a5948670506956d17d0ee8a9e2e194df1bee1acf..5561ba521e05ee9d697d97e28cfe992b18b3d53e 100644 (file)
@@ -1,3 +1,4 @@
 create-diff-object
+prelink
 *.o
 *.d
index 3741f6db95dc25145da37c3f51e7c1c2ff91fe35..aa5d5b0f324f27276fef703da7a9fb5bf55bdf6f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,9 +7,10 @@ CC    = gcc
 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)
 
@@ -18,8 +19,11 @@ 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
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..bed6b5f
--- /dev/null
+++ b/common.c
@@ -0,0 +1,746 @@
+#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, &sections_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");
+}
diff --git a/common.h b/common.h
new file mode 100644 (file)
index 0000000..b8369a5
--- /dev/null
+++ b/common.h
@@ -0,0 +1,160 @@
+#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_ */
index 2b21653ef012c1dada3c4dba7b7642ca0f5e4e8c..b3f7f9eeccd36f26a1f2447a3547cb4edf95628c 100644 (file)
 #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, &sections_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)
 {
@@ -1250,47 +728,6 @@ static void xsplice_compare_correlated_elements(struct xsplice_elf *kelf)
        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;
@@ -1691,54 +1128,6 @@ static void xsplice_print_changes(struct xsplice_elf *kelf)
        }
 }
 
-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;
@@ -2060,11 +1449,6 @@ static int is_local_func_sym(struct symbol *sym)
        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);
@@ -2103,261 +1487,6 @@ static void xsplice_reindex_elements(struct xsplice_elf *kelf)
        }
 }
 
-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;
diff --git a/prelink.c b/prelink.c
new file mode 100644 (file)
index 0000000..44cd6fc
--- /dev/null
+++ b/prelink.c
@@ -0,0 +1,181 @@
+/*
+ * 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;
+}
index ff8e1c0db90ca5b662b000efab02adf0dbb48c80..b93d240356bb68f09a3d62cb729332e38a937a58 100755 (executable)
@@ -136,7 +136,8 @@ function create_patch()
     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() {