Commit b6668af6 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

piegen: Implement tool building, v2

Here we simply build piegen tool which gonna be used
to generate parasite code safe to rellocate. The tool
is taking object file as an argument, parses it and
generates C file with rellocations encoded in form
suitable for fast appliance.

Currently only x86-32 x86-64 is supported.

v2 (by ldufour@):
 - Filter PIEGEN
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 7a981334
......@@ -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
......
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
#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"
#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"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <elf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#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;
}
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <fcntl.h>
#include <elf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#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;
}
#ifndef __ELFTIL_H__
#define __ELFTIL_H__
#include <stdio.h>
#include <unistd.h>
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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment