From: Ross Lagerwall Date: Tue, 20 Oct 2015 15:51:37 +0000 (+0100) Subject: Add prelink tool X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=8db6ae82684f00711cba848c4b6631b7597f81a1;p=livepatch-build-tools.git Add prelink tool 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. --- diff --git a/.gitignore b/.gitignore index a594867..5561ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ create-diff-object +prelink *.o *.d diff --git a/Makefile b/Makefile index 3741f6d..aa5d5b0 100644 --- 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 index 0000000..bed6b5f --- /dev/null +++ b/common.c @@ -0,0 +1,746 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..b8369a5 --- /dev/null +++ b/common.h @@ -0,0 +1,160 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include + +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_ */ diff --git a/create-diff-object.c b/create-diff-object.c index 2b21653..b3f7f9e 100644 --- a/create-diff-object.c +++ b/create-diff-object.c @@ -50,529 +50,7 @@ #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) { @@ -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 index 0000000..44cd6fc --- /dev/null +++ b/prelink.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 Ross Lagerwall + * + * 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 . + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/xsplice-build b/xsplice-build index ff8e1c0..b93d240 100755 --- a/xsplice-build +++ b/xsplice-build @@ -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() {