diff --git a/Makefile b/Makefile index 9f636243d..2a26e1eca 100644 --- a/Makefile +++ b/Makefile @@ -142,6 +142,9 @@ ARCH-LIB := $(ARCH_DIR)/crtools.built-in.o CRIU-SO := libcriu CRIU-LIB := lib/$(CRIU-SO).so CRIU-INC := lib/criu.h include/criu-plugin.h include/criu-log.h protobuf/rpc.proto +ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) +PIEGEN := pie/piegen/piegen +endif export CC MAKE CFLAGS LIBS SRCARCH DEFINES MAKEFLAGS CRIU-SO export SRC_DIR SYSCALL-LIB SH RM ARCH_DIR OBJCOPY LDARCH LD @@ -184,9 +187,20 @@ $(ARCH_DIR)/%:: protobuf config $(ARCH_DIR): protobuf config $(Q) $(MAKE) $(build)=$(ARCH_DIR) all -pie/%:: $(ARCH_DIR) +ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) +pie/piegen/%: + $(Q) $(MAKE) $(build)=pie/piegen $@ +pie/piegen: + $(Q) $(MAKE) $(build)=pie/piegen all +$(PIEGEN): pie/piegen/built-in.o + $(E) " LINK " $@ + $(Q) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ +.PHONY: pie/piegen +endif + +pie/%:: $(ARCH_DIR) $(PIEGEN) $(Q) $(MAKE) $(build)=pie $@ -pie: $(ARCH_DIR) +pie: $(ARCH_DIR) $(PIEGEN) $(Q) $(MAKE) $(build)=pie all %.o %.i %.s %.d: $(VERSION_HEADER) pie @@ -235,6 +249,7 @@ clean-built: $(Q) $(RM) $(VERSION_HEADER) $(Q) $(MAKE) $(build)=$(ARCH_DIR) clean $(Q) $(MAKE) $(build)=protobuf clean + $(Q) $(MAKE) $(build)=pie/piegen clean $(Q) $(MAKE) $(build)=pie clean $(Q) $(MAKE) $(build)=lib clean $(Q) $(MAKE) $(build-crtools)=. clean diff --git a/pie/piegen/Makefile b/pie/piegen/Makefile new file mode 100644 index 000000000..dd54c3f0b --- /dev/null +++ b/pie/piegen/Makefile @@ -0,0 +1,14 @@ +CFLAGS += -iquote pie/piegen + +obj-y += main.o +ifneq ($(filter i386 ia32 x86_64, $(ARCH)),) +obj-y += elf-x86-32.o +obj-y += elf-x86-64.o +endif + +cleanup-y += $(obj)/piegen +cleanup-y += $(obj)/*.o + +ifneq ($(MAKECMDGOALS),clean) +incdeps := y +endif diff --git a/pie/piegen/elf-x86-32.c b/pie/piegen/elf-x86-32.c new file mode 100644 index 000000000..413113ef3 --- /dev/null +++ b/pie/piegen/elf-x86-32.c @@ -0,0 +1,16 @@ +#define ELF_X86_32 +#define handle_elf handle_elf_x86_32 + +#define Ehdr_t Elf32_Ehdr +#define Shdr_t Elf32_Shdr +#define Sym_t Elf32_Sym +#define Rel_t Elf32_Rel +#define Rela_t Elf32_Rela + +#define ELF_ST_TYPE ELF32_ST_TYPE +#define ELF_ST_BIND ELF32_ST_BIND + +#define ELF_R_SYM ELF32_R_SYM +#define ELF_R_TYPE ELF32_R_TYPE + +#include "elf.c" diff --git a/pie/piegen/elf-x86-64.c b/pie/piegen/elf-x86-64.c new file mode 100644 index 000000000..8ba26672b --- /dev/null +++ b/pie/piegen/elf-x86-64.c @@ -0,0 +1,16 @@ +#define ELF_X86_64 +#define handle_elf handle_elf_x86_64 + +#define Ehdr_t Elf64_Ehdr +#define Shdr_t Elf64_Shdr +#define Sym_t Elf64_Sym +#define Rel_t Elf64_Rel +#define Rela_t Elf64_Rela + +#define ELF_ST_TYPE ELF64_ST_TYPE +#define ELF_ST_BIND ELF64_ST_BIND + +#define ELF_R_SYM ELF64_R_SYM +#define ELF_R_TYPE ELF64_R_TYPE + +#include "elf.c" diff --git a/pie/piegen/elf.c b/pie/piegen/elf.c new file mode 100644 index 000000000..5812c4ca3 --- /dev/null +++ b/pie/piegen/elf.c @@ -0,0 +1,338 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "asm-generic/int.h" + +#include "compiler.h" +#include "piegen.h" + +static bool __ptr_oob(const void *ptr, const void *start, const size_t size) +{ + const void *end = (const void *)((const unsigned long)start + size); + return ptr > end || ptr < start; +} + +static bool test_pointer(const void *ptr, const void *start, const size_t size, + const char *name, const char *file, const int line) +{ + if (__ptr_oob(ptr, start, size)) { + pr_err("Corrupted pointer %p (%s) at %s:%d\n", + ptr, name, file, line); + return true; + } + return false; +} + +#define ptr_func_exit(__ptr) \ + do { \ + if (test_pointer((__ptr), mem, size, #__ptr, \ + __FILE__, __LINE__)) { \ + free(sec_hdrs); \ + return -1; \ + } \ + } while (0) + +int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) +{ + const char *symstrings = NULL; + Shdr_t *symtab_hdr = NULL; + Sym_t *symbols = NULL; + Ehdr_t *hdr = mem; + + Shdr_t *secstrings_hdr = NULL; + Shdr_t *strtab_hdr = NULL; + Shdr_t **sec_hdrs = NULL; + const char *secstrings; + + size_t i, k, nr_gotpcrel = 0; + + pr_debug("Header\n------------\n"); + pr_debug("\ttype 0x%x machine 0x%x version 0x%x\n", + (unsigned)hdr->e_type, (unsigned)hdr->e_machine, (unsigned)hdr->e_version); + +#ifdef ELF_X86_64 + if (hdr->e_type != ET_REL || hdr->e_machine != EM_X86_64 || hdr->e_version != EV_CURRENT) { + pr_err("Unsupported header detected\n"); + goto err; + } +#endif + +#ifdef ELF_X86_32 + if (hdr->e_type != ET_REL || hdr->e_machine != EM_386 || hdr->e_version != EV_CURRENT) { + pr_err("Unsupported header detected\n"); + goto err; + } +#endif + + sec_hdrs = malloc(sizeof(*sec_hdrs) * hdr->e_shnum); + if (!sec_hdrs) { + pr_err("No memory for section headers\n"); + goto err; + } + + secstrings_hdr = mem + hdr->e_shoff + hdr->e_shentsize * hdr->e_shstrndx; + secstrings = mem + secstrings_hdr->sh_offset; + ptr_func_exit(secstrings_hdr); + ptr_func_exit(secstrings); + + pr_debug("Sections\n------------\n"); + for (i = 0; i < hdr->e_shnum; i++) { + Shdr_t *sh = mem + hdr->e_shoff + hdr->e_shentsize * i; + ptr_func_exit(sh); + + if (sh->sh_type == SHT_SYMTAB) + symtab_hdr = sh; + + ptr_func_exit(&secstrings[sh->sh_name]); + pr_debug("\t index %-2zd type 0x%-2x name %s\n", i, + (unsigned)sh->sh_type, &secstrings[sh->sh_name]); + + sec_hdrs[i] = sh; + } + + if (!symtab_hdr) { + pr_err("No symbol table present\n"); + goto err; + } + + if (!symtab_hdr->sh_link || symtab_hdr->sh_link >= hdr->e_shnum) { + pr_err("Corrupted symtab header\n"); + goto err; + } + + pr_debug("Symbols\n------------\n"); + strtab_hdr = sec_hdrs[symtab_hdr->sh_link]; + ptr_func_exit(strtab_hdr); + + symbols = mem + symtab_hdr->sh_offset; + ptr_func_exit(symbols); + symstrings = mem + strtab_hdr->sh_offset; + ptr_func_exit(symstrings); + + if (sizeof(*symbols) != symtab_hdr->sh_entsize) { + pr_err("Symbol table align differ\n"); + goto err; + } + + pr_out("/* Autogenerated from %s */\n", opts->input_filename); + for (i = 0; i < symtab_hdr->sh_size / symtab_hdr->sh_entsize; i++) { + Sym_t *sym = &symbols[i]; + const char *name; + Shdr_t *sh_src; + + ptr_func_exit(sym); + name = &symstrings[sym->st_name]; + ptr_func_exit(name); + + if (*name) { + pr_debug("\ttype 0x%-2x bind 0x%-2x shndx 0x%-4x value 0x%-2lx name %s\n", + (unsigned)ELF_ST_TYPE(sym->st_info), (unsigned)ELF_ST_BIND(sym->st_info), + (unsigned)sym->st_shndx, (unsigned long)sym->st_value, name); + if (strncmp(name, "__export", 8)) + continue; + if (sym->st_shndx && sym->st_shndx < hdr->e_shnum) { + sh_src = sec_hdrs[sym->st_shndx]; + ptr_func_exit(sh_src); +#if 0 + if (ELF_ST_TYPE(sym->st_info) == STT_FUNC || + ELF_ST_TYPE(sym->st_info) == STT_OBJECT) +#endif + pr_out("#define %s%s 0x%lx\n", + opts->prefix_name, name, + (unsigned long)(sym->st_value + sh_src->sh_offset)); + } + } + } + + pr_out("typedef struct {\n" + " unsigned int offset;\n" + " unsigned int type;\n" + " long addend;\n" + " long value;\n" + "} %s;\nstatic __maybe_unused %s %s[] = {\n", + opts->type_name, opts->type_name, opts->var_name); + + pr_debug("Relocations\n------------\n"); + for (i = 0; i < hdr->e_shnum; i++) { + Shdr_t *sh = sec_hdrs[i]; + Shdr_t *sh_rel; + + if (sh->sh_type != SHT_REL && sh->sh_type != SHT_RELA) + continue; + + sh_rel = sec_hdrs[sh->sh_info]; + ptr_func_exit(sh_rel); + + pr_debug("\tsection %2zd type 0x%-2x link 0x%-2x info 0x%-2x name %s\n", i, + (unsigned)sh->sh_type, (unsigned)sh->sh_link, + (unsigned)sh->sh_info, &secstrings[sh->sh_name]); + + for (k = 0; k < sh->sh_size / sh->sh_entsize; k++) { + s64 __maybe_unused addend64, __maybe_unused value64; + s32 addend32, value32; + unsigned long place; + const char *name; + Shdr_t *sh_src; + void *where; + Sym_t *sym; + + union { + Rel_t rel; + Rela_t rela; + } *r = mem + sh->sh_offset + sh->sh_entsize * k; + ptr_func_exit(r); + + sym = &symbols[ELF_R_SYM(r->rel.r_info)]; + ptr_func_exit(sym); + + name = &symstrings[sym->st_name]; + ptr_func_exit(name); + + where = mem + sh_rel->sh_offset + r->rel.r_offset; + ptr_func_exit(where); + + sh_src = sec_hdrs[sym->st_shndx]; + ptr_func_exit(sh_src); + + pr_debug("\t\tr_offset 0x%-4lx r_info 0x%-4lx / sym 0x%-2lx type 0x%-2lx symsecoff 0x%-4lx\n", + (unsigned long)r->rel.r_offset, (unsigned long)r->rel.r_info, + (unsigned long)ELF_R_SYM(r->rel.r_info), + (unsigned long)ELF_R_TYPE(r->rel.r_info), + (unsigned long)sh_src->sh_offset); + + if (sym->st_shndx == SHN_UNDEF) + continue; + + ptr_func_exit((mem + sh_rel->sh_offset + r->rel.r_offset)); + if (sh->sh_type == SHT_REL) { + addend32 = *(s32 *)where; + addend64 = *(s64 *)where; + } else { + addend32 = (s32)r->rela.r_addend; + addend64 = (s64)r->rela.r_addend; + } + + place = where - mem; + + pr_debug("\t\t\tvalue 0x%-8lx addend32 %-4d addend64 %-8ld symname %s\n", + (unsigned long)sym->st_value, addend32, (long)addend64, name); + + value32 = (s32)sh_src->sh_offset + (s32)sym->st_value; + value64 = (s64)sh_src->sh_offset + (s64)sym->st_value; + + switch (ELF_R_TYPE(r->rel.r_info)) { +#ifdef ELF_X86_64 + case R_X86_64_32: /* Symbol + Addend (4 bytes) */ + pr_debug("\t\t\t\tR_X86_64_32 at 0x%-4lx val 0x%x\n", place, value32); + pr_out(" { .offset = 0x%-8x, .type = 0, " + ".addend = 0 , .value = 0x%-16x, }, /* R_X86_64_32 */\n", + (unsigned int)place, value32 + addend32); + break; + case R_X86_64_64: /* Symbol + Addend (8 bytes) */ + pr_debug("\t\t\t\tR_X86_64_64 at 0x%-4lx val 0x%lx\n", place, value64); + pr_out(" { .offset = 0x%-8x, .type = 1, " + ".addend = 0 , .value = 0x%-16lx, }, /* R_X86_64_64 */\n", + (unsigned int)place, (long)(value64 + addend64)); + break; + case R_X86_64_PC32: /* Symbol + Addend - Place (4 bytes) */ + pr_debug("\t\t\t\tR_386_PC32 at 0x%-4lx val 0x%x\n", place, value32 + addend32 - (s32)place); + /* + * R_X86_64_PC32 are relative, patch them inplace. + */ + *((s32 *)where) = value32 + addend32 - place; + break; + case R_X86_64_PLT32: /* ProcLinkage + Addend - Place (4 bytes) */ + pr_debug("\t\t\t\tR_386_PLT32 at 0x%-4lx val 0x%x\n", place, value32 + addend32 - (s32)place); + /* + * R_X86_64_PLT32 are relative, patch them inplace. + */ + *((s32 *)where) = value32 + addend32 - place; + break; + case R_X86_64_GOTPCREL: /* SymbolOffsetInGot + GOT + Addend - Place (4 bytes) */ + pr_debug("\t\t\t\tR_X86_64_GOTPCREL at 0x%-4lx val 0x%x\n", place, value32); + pr_out(" { .offset = 0x%-8x, .type = 3, " + ".addend = %-8d, .value = 0x%-16x, }, /* R_X86_64_GOTPCREL */\n", + (unsigned int)place, addend32, value32); + nr_gotpcrel++; + break; +#endif +#ifdef ELF_X86_32 + case R_386_32: /* Symbol + Addend */ + pr_debug("\t\t\t\tR_386_32 at 0x%-4lx val 0x%x\n", place, value32 + addend32); + pr_out(" { .offset = 0x%-8x, .type = 0, " + ".addend = %-4d, .value = 0x%x, },\n", + (unsigned int)place, addend32, value32); + break; + case R_386_PC32: /* Symbol + Addend - Place */ + pr_debug("\t\t\t\tR_386_PC32 at 0x%-4lx val 0x%x\n", place, value32 + addend32 - (s32)place); + /* + * R_386_PC32 are relative, patch them inplace. + */ + *((s32 *)where) = value32 + addend32 - place; + break; +#endif + default: + pr_err("Unsupported relocation\n"); + goto err; + } + } + } + pr_out("};\n"); + pr_out("static __maybe_unused size_t %s = %zd;\n", opts->nrgotpcrel_name, nr_gotpcrel); + + pr_out("static __maybe_unused const char %s[] = {\n\t", opts->stream_name); + for (i = 0; i < ALIGN(size, 8); i++) { + if (i && (i % 8) == 0) + pr_out("\n\t"); + if (i < size) + pr_out("0x%02x,", ((unsigned char *)mem)[i]); + else + pr_out("0x00,"); + } + pr_out("};\n"); + +#if defined(CONFIG_X86_64) || defined(CONFIG_X86_32) +pr_out( +"static __maybe_unused void elf_apply_relocs(void *mem, size_t size, %s *elf_relocs, size_t nr_relocs)\n" +"{\n" +" size_t i, j;\n" +"\n" +" for (i = 0, j = 0; i < ARRAY_SIZE(elf_relocs); i++) {\n" +" if (elf_relocs[i].type) {\n" +" long *where = mem + elf_relocs[i].offset;\n" +" long *p = mem + size;\n" +"\n" +" if (elf_relocs[i].type & 2) {\n" +" int *value = (int *)where;\n" +" int rel;\n" +"\n" +" p[j] = (long)mem + elf_relocs[i].value;\n" +" rel = (unsigned)((void *)&p[j] - (void *)mem) - elf_relocs[i].offset - elf_relocs[i].addend;\n" +"\n" +" *value = rel;\n" +" j++;\n" +" } else\n" +" *where = elf_relocs[i].value + elf_relocs[i].addend + (unsigned long)mem;\n" +" } else {\n" +" int *where = (mem + elf_relocs[i].offset);\n" +" *where = elf_relocs[i].value + elf_relocs[i].addend + (unsigned long)mem;\n" +" }\n" +" }\n" +"}\n", opts->type_name); +#endif + +err: + free(sec_hdrs); + return -1; +} diff --git a/pie/piegen/main.c b/pie/piegen/main.c new file mode 100644 index 000000000..45bbf2454 --- /dev/null +++ b/pie/piegen/main.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "compiler.h" +#include "config.h" +#include "piegen.h" + +piegen_opt_t opts = { + .input_filename = "file.o", + .stream_name = "stream", + .type_name = "elf_reloc_t", + .prefix_name = "__", + .var_name = "elf_relocs", + .nrgotpcrel_name = "nr_gotpcrel", +}; + +static int handle_elf(const piegen_opt_t *opts, void *mem, size_t size) +{ +#if defined(CONFIG_X86_32) || defined(CONFIG_X86_64) + unsigned char elf_ident_x86_32[EI_NIDENT] = { + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + unsigned char elf_ident_x86_64[EI_NIDENT] = { + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + if (memcmp(mem, elf_ident_x86_32, sizeof(elf_ident_x86_32)) == 0) + return handle_elf_x86_32(opts, mem, size); + else if (memcmp(mem, elf_ident_x86_64, sizeof(elf_ident_x86_64)) == 0) + return handle_elf_x86_64(opts, mem, size); +#endif + + pr_err("Unsupported Elf format detected\n"); + return -1; +} + +/* + * That;s the tool to generate patches object files. + */ +int main(int argc, char *argv[]) +{ + struct stat st; + int opt, idx; + void *mem; + int fd; + + static const char short_opts[] = "f:s:t:p:v:h"; + static struct option long_opts[] = { + { "file", required_argument, 0, 'f' }, + { "stream", required_argument, 0, 's' }, + { "type", required_argument, 0, 't' }, + { "sym-prefix", required_argument, 0, 'p' }, + { "variable", required_argument, 0, 'v' }, + { "help", required_argument, 0, 'h' }, + { }, + }; + + if (argc < 3) + goto usage; + + while (1) { + idx = -1; + opt = getopt_long(argc, argv, short_opts, long_opts, &idx); + if (opt == -1) + break; + switch (opt) { + case 'f': + opts.input_filename = optarg; + break; + case 's': + opts.stream_name = optarg; + break; + case 'p': + opts.prefix_name = optarg; + break; + case 't': + opts.type_name = optarg; + break; + case 'v': + opts.var_name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + fd = open(opts.input_filename, O_RDONLY); + if (fd < 0) { + pr_perror("Can't open file %s", opts.input_filename); + goto err; + } + + if (fstat(fd, &st)) { + pr_perror("Can't stat file %s", opts.input_filename); + goto err; + } + + mem = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0); + if (mem == MAP_FAILED) { + pr_perror("Can't mmap file %s", opts.input_filename); + goto err; + } + + if (handle_elf(&opts, mem, st.st_size)) + goto err; + return 1; +usage: + printf("Usage: %s -f filename\n", argv[0]); +err: + return 0; +} diff --git a/pie/piegen/piegen.h b/pie/piegen/piegen.h new file mode 100644 index 000000000..10505d758 --- /dev/null +++ b/pie/piegen/piegen.h @@ -0,0 +1,34 @@ +#ifndef __ELFTIL_H__ +#define __ELFTIL_H__ + +#include +#include + +typedef struct { + char *input_filename; + char *stream_name; + char *type_name; + char *prefix_name; + char *var_name; + char *nrgotpcrel_name; +} piegen_opt_t; + +extern piegen_opt_t opts; + +#if defined(CONFIG_X86_32) || defined(CONFIG_X86_64) +extern int handle_elf_x86_32(const piegen_opt_t *opts, void *mem, size_t size); +extern int handle_elf_x86_64(const piegen_opt_t *opts, void *mem, size_t size); +#endif + +#define pr_out(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__) + +#if 0 +# define pr_debug(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +#else +# define pr_debug(fmt, ...) +#endif + +#define pr_err(fmt, ...) fprintf(stderr, "Error (%s:%d): "fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#define pr_perror(fmt, ...) fprintf(stderr, "Error (%s:%d): "fmt "%m\n", __FILE__, __LINE__, ##__VA_ARGS__) + +#endif /* __ELFTIL_H__ */