compel: Initial commit for standalone tool

The compel component is a replacement for several aspects of CRIU
functionality: binary blobs generation for PIE parasite/restore code,
and a library for parasite code injection and execution (to be implemented).

In the commit we rather shuffle compel into own directory and
use it for

1) Fetching cflags when compiling PIE blobs
2) Use its "piegen" functionality to generate blobs themselves.

Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
This commit is contained in:
Cyrill Gorcunov 2016-04-05 18:41:15 +03:00 committed by Andrei Vagin
parent dbc2edb83a
commit acfa85ba5a
15 changed files with 42 additions and 47 deletions

20
compel/Makefile Normal file
View file

@ -0,0 +1,20 @@
include $(SRC_DIR)/Makefile.versions
ccflags-y += -iquote criu/include
ccflags-y += -iquote compel/include
ccflags-y += -DCOMPEL_VERSION=\"$(COMPEL_SO_VERSION_MAJOR).$(COMPEL_SO_VERSION_MINOR)\"
host-ccflags-y += $(filter-out -pg $(CFLAGS-GCOV),$(ccflags-y))
HOSTCFLAGS += $(filter-out -pg $(CFLAGS-GCOV),$(WARNINGS) $(DEFINES))
HOSTLDFLAGS += $(filter-out -pg $(CFLAGS-GCOV),$(LDFLAGS))
hostprogs-y += compel
compel-objs += src/main.o
ifneq ($(filter ia32 x86, $(ARCH)),)
compel-objs += src/elf-x86-32.o
compel-objs += src/elf-x86-64.o
endif
ifeq ($(SRCARCH),ppc64)
compel-objs += src/elf-ppc64.o
endif

36
compel/include/piegen.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef __ELFTIL_H__
#define __ELFTIL_H__
#include <stdio.h>
#include <unistd.h>
typedef struct {
char *input_filename;
char *output_filename;
char *uapi_dir;
char *stream_name;
char *prefix_name;
char *var_name;
char *nrgotpcrel_name;
} piegen_opt_t;
extern piegen_opt_t opts;
extern FILE *fout;
#if defined(CONFIG_X86_32) || defined(CONFIG_X86_64)
extern int handle_elf_x86_32(void *mem, size_t size);
extern int handle_elf_x86_64(void *mem, size_t size);
#endif
#if defined(CONFIG_PPC64)
extern int handle_elf_ppc64(void *mem, size_t size);
#endif
#define pr_out(fmt, ...) fprintf(fout, fmt, ##__VA_ARGS__)
#define pr_debug(fmt, ...) printf("%s: "fmt, opts.stream_name, ##__VA_ARGS__)
#define pr_err(fmt, ...) fprintf(stderr, "%s: Error (%s:%d): "fmt, opts.stream_name, __FILE__, __LINE__, ##__VA_ARGS__)
#define pr_perror(fmt, ...) fprintf(stderr, "%s: Error (%s:%d): "fmt ": %m\n", opts.stream_name, __FILE__, __LINE__, ##__VA_ARGS__)
#endif /* __ELFTIL_H__ */

View file

@ -0,0 +1,15 @@
#ifndef __PIEGEN_TYPES_H__
#define __PIEGEN_TYPES_H__
#define PIEGEN_TYPE_INT (1u << 0)
#define PIEGEN_TYPE_LONG (1u << 1)
#define PIEGEN_TYPE_GOTPCREL (1u << 2)
typedef struct {
unsigned int offset;
unsigned int type;
long addend;
long value;
} elf_reloc_t;
#endif /* __PIEGEN_TYPES_H__ */

16
compel/src/elf-ppc64.c Normal file
View file

@ -0,0 +1,16 @@
#define ELF_PPC64
#define handle_elf handle_elf_ppc64
#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"

16
compel/src/elf-x86-32.c Normal file
View file

@ -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"

16
compel/src/elf-x86-64.c Normal file
View file

@ -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"

512
compel/src/elf.c Normal file
View file

@ -0,0 +1,512 @@
#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 "common/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)
#ifdef ELF_PPC64
static int do_relative_toc(long value, uint16_t *location,
unsigned long mask, int complain_signed)
{
if (complain_signed && (value + 0x8000 > 0xffff)) {
pr_err("TOC16 relocation overflows (%ld)\n", value);
return -1;
}
if ((~mask & 0xffff) & value) {
pr_err("bad TOC16 relocation (%ld) (0x%lx)\n", value, (~mask & 0xffff) & value);
return -1;
}
*location = (*location & ~mask) | (value & mask);
return 0;
}
#endif
int handle_elf(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;
#ifdef ELF_PPC64
s64 toc_offset = 0;
#endif
pr_debug("Header\n");
pr_debug("------------\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");
pr_debug("------------\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;
#ifdef ELF_PPC64
if (!strcmp(&secstrings[sh->sh_name], ".toc")) {
toc_offset = sh->sh_addr + 0x8000;
pr_debug("\t\tTOC offset 0x%lx\n", toc_offset);
}
#endif
}
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");
pr_debug("------------\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);
pr_out("#include \"%s/types.h\"\n", opts.uapi_dir);
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);
#ifdef ELF_PPC64
if (!sym->st_value && !strncmp(name, ".TOC.", 6)) {
if (!toc_offset) {
pr_err("No TOC pointer\n");
goto err;
}
sym->st_value = toc_offset;
continue;
}
#endif
if (strncmp(name, "__export", 8))
continue;
if ((sym->st_shndx && sym->st_shndx < hdr->e_shnum) || sym->st_shndx == SHN_ABS) {
if (sym->st_shndx == SHN_ABS) {
sh_src = NULL;
} else {
sh_src = sec_hdrs[sym->st_shndx];
ptr_func_exit(sh_src);
}
pr_out("#define %s%s 0x%lx\n",
opts.prefix_name, name,
(unsigned long)(sym->st_value + (sh_src ? sh_src->sh_addr : 0)));
}
}
}
pr_out("static __maybe_unused elf_reloc_t %s[] = {\n", opts.var_name);
pr_debug("Relocations\n");
pr_debug("------------\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;
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);
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_rel->sh_addr);
if (sym->st_shndx == SHN_UNDEF) {
#ifdef ELF_PPC64
/* On PowerPC, TOC symbols appear to be
* undefined but should be processed as well.
* Their type is STT_NOTYPE, so report any
* other one.
*/
if (ELF32_ST_TYPE(sym->st_info) != STT_NOTYPE
|| strncmp(name, ".TOC.", 6)) {
pr_err("Unexpected undefined symbol:%s\n", name);
goto err;
}
#else
continue;
#endif
}
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 = sh_rel->sh_addr + r->rel.r_offset;
pr_debug("\t\t\tvalue 0x%-8lx addend32 %-4d addend64 %-8ld place %-8lx symname %s\n",
(unsigned long)sym->st_value, addend32, (long)addend64, (long)place, name);
if (sym->st_shndx == SHN_ABS) {
value32 = (s32)sym->st_value;
value64 = (s64)sym->st_value;
} else {
Shdr_t *sh_src;
if ((unsigned)sym->st_shndx > (unsigned)hdr->e_shnum) {
pr_err("Unexpected symbol section index %u/%u\n",
(unsigned)sym->st_shndx, hdr->e_shnum);
goto err;
}
sh_src = sec_hdrs[sym->st_shndx];
ptr_func_exit(sh_src);
value32 = (s32)sh_src->sh_addr + (s32)sym->st_value;
value64 = (s64)sh_src->sh_addr + (s64)sym->st_value;
}
#ifdef ELF_PPC64
/* Snippet from the OpenPOWER ABI for Linux Supplement:
* The OpenPOWER ABI uses the three most-significant bits in the symbol
* st_other field specifies the number of instructions between a function's
* global entry point and local entry point. The global entry point is used
* when it is necessary to set up the TOC pointer (r2) for the function. The
* local entry point is used when r2 is known to already be valid for the
* function. A value of zero in these bits asserts that the function does
* not use r2.
* The st_other values have the following meanings:
* 0 and 1, the local and global entry points are the same.
* 2, the local entry point is at 1 instruction past the global entry point.
* 3, the local entry point is at 2 instructions past the global entry point.
* 4, the local entry point is at 4 instructions past the global entry point.
* 5, the local entry point is at 8 instructions past the global entry point.
* 6, the local entry point is at 16 instructions past the global entry point.
* 7, reserved.
*
* Here we are only handle the case '3' which is the most commonly seen.
*/
#define LOCAL_OFFSET(s) ((s->st_other >> 5) & 0x7)
if (LOCAL_OFFSET(sym)) {
if (LOCAL_OFFSET(sym) != 3) {
pr_err("Unexpected local offset value %d\n",
LOCAL_OFFSET(sym));
goto err;
}
pr_debug("\t\t\tUsing local offset\n");
value64 += 8;
value32 += 8;
}
#endif
switch (ELF_R_TYPE(r->rel.r_info)) {
#ifdef ELF_PPC64
case R_PPC64_REL24:
/* Update PC relative offset, linker has not done this yet */
pr_debug("\t\t\tR_PPC64_REL24 at 0x%-4lx val 0x%lx\n",
place, value64);
/* Convert value to relative */
value64 -= place;
if (value64 + 0x2000000 > 0x3ffffff || (value64 & 3) != 0) {
pr_err("REL24 %li out of range!\n", (long int)value64);
goto err;
}
/* Only replace bits 2 through 26 */
*(uint32_t *)where = (*(uint32_t *)where & ~0x03fffffc) |
(value64 & 0x03fffffc);
break;
case R_PPC64_ADDR32:
pr_debug("\t\t\tR_PPC64_ADDR32 at 0x%-4lx val 0x%x\n",
place, (unsigned int)(value32 + addend32));
pr_out(" { .offset = 0x%-8x, .type = PIEGEN_TYPE_INT, "
" .addend = %-8d, .value = 0x%-16x, "
"}, /* R_PPC64_ADDR32 */\n",
(unsigned int) place, addend32, value32);
break;
case R_PPC64_ADDR64:
case R_PPC64_REL64:
pr_debug("\t\t\tR_PPC64_ADDR64 at 0x%-4lx val 0x%lx\n",
place, value64 + addend64);
pr_out("\t{ .offset = 0x%-8x, .type = PIEGEN_TYPE_LONG,"
" .addend = %-8ld, .value = 0x%-16lx, "
"}, /* R_PPC64_ADDR64 */\n",
(unsigned int) place, (long)addend64, (long)value64);
break;
case R_PPC64_TOC16_HA:
pr_debug("\t\t\tR_PPC64_TOC16_HA at 0x%-4lx val 0x%lx\n",
place, value64 + addend64 - toc_offset + 0x8000);
if (do_relative_toc((value64 + addend64 - toc_offset + 0x8000) >> 16,
where, 0xffff, 1))
goto err;
break;
case R_PPC64_TOC16_LO:
pr_debug("\t\t\tR_PPC64_TOC16_LO at 0x%-4lx val 0x%lx\n",
place, value64 + addend64 - toc_offset);
if (do_relative_toc(value64 + addend64 - toc_offset,
where, 0xffff, 1))
goto err;
break;
case R_PPC64_TOC16_LO_DS:
pr_debug("\t\t\tR_PPC64_TOC16_LO_DS at 0x%-4lx val 0x%lx\n",
place, value64 + addend64 - toc_offset);
if (do_relative_toc(value64 + addend64 - toc_offset,
where, 0xfffc, 0))
goto err;
break;
case R_PPC64_REL16_HA:
value64 += addend64 - place;
pr_debug("\t\t\tR_PPC64_REL16_HA at 0x%-4lx val 0x%lx\n",
place, value64);
/* check that we are dealing with the addis 2,12 instruction */
if (((*(uint32_t*)where) & 0xffff0000) != 0x3c4c0000) {
pr_err("Unexpected instruction for R_PPC64_REL16_HA\n");
goto err;
}
*(uint16_t *)where = ((value64 + 0x8000) >> 16) & 0xffff;
break;
case R_PPC64_REL16_LO:
value64 += addend64 - place;
pr_debug("\t\t\tR_PPC64_REL16_LO at 0x%-4lx val 0x%lx\n",
place, value64);
/* check that we are dealing with the addi 2,2 instruction */
if (((*(uint32_t*)where) & 0xffff0000) != 0x38420000) {
pr_err("Unexpected instruction for R_PPC64_REL16_LO\n");
goto err;
}
*(uint16_t *)where = value64 & 0xffff;
break;
#endif /* ELF_PPC64 */
#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 = PIEGEN_TYPE_INT, "
".addend = %-8d, .value = 0x%-16x, }, /* R_X86_64_32 */\n",
(unsigned int)place, addend32, value32);
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, (long)value64);
pr_out(" { .offset = 0x%-8x, .type = PIEGEN_TYPE_LONG, "
".addend = %-8ld, .value = 0x%-16lx, }, /* R_X86_64_64 */\n",
(unsigned int)place, (long)addend64, (long)value64);
break;
case R_X86_64_PC32: /* Symbol + Addend - Place (4 bytes) */
pr_debug("\t\t\t\tR_X86_64_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_X86_64_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 = PIEGEN_TYPE_LONG | PIEGEN_TYPE_GOTPCREL, "
".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 = PIEGEN_TYPE_INT, "
".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, k=0; i < hdr->e_shnum; i++) {
Shdr_t *sh = sec_hdrs[i];
unsigned char *shdata;
size_t j;
if (!(sh->sh_flags & SHF_ALLOC) || !sh->sh_size)
continue;
shdata = mem + sh->sh_offset;
pr_debug("Copying section '%s'\n" \
"\tstart:0x%lx (gap:0x%lx) size:0x%lx\n",
&secstrings[sh->sh_name], (unsigned long) sh->sh_addr,
(unsigned long)(sh->sh_addr - k), (unsigned long) sh->sh_size);
/* write 0 in the gap between the 2 sections */
for (;k < sh->sh_addr; k++) {
if (k && (k % 8) == 0)
pr_out("\n\t");
pr_out("0x00,");
}
for (j=0; j < sh->sh_size; j++, k++) {
if (k && (k % 8) == 0)
pr_out("\n\t");
pr_out("%#02x,", shdata[j]);
}
}
pr_out("};\n");
free(sec_hdrs);
return 0;
err:
free(sec_hdrs);
return -1;
}

235
compel/src/main.c Normal file
View file

@ -0,0 +1,235 @@
#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 "common/compiler.h"
#include "config.h"
#include "piegen.h"
static const char compel_cflags_pie[] = "-fpie -Wa,--noexecstack -fno-stack-protector";
static const char compel_cflags_nopic[] = "-fno-pic -Wa,--noexecstack -fno-stack-protector";
static const char compel_ldflags[] = "-r";
piegen_opt_t opts = {
.input_filename = NULL,
.uapi_dir = "piegen/uapi",
.stream_name = "stream",
.prefix_name = "__",
.var_name = "elf_relocs",
.nrgotpcrel_name = "nr_gotpcrel",
};
FILE *fout;
static int handle_elf(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(mem, size);
else if (memcmp(mem, elf_ident_x86_64, sizeof(elf_ident_x86_64)) == 0)
return handle_elf_x86_64(mem, size);
#endif
#if defined(CONFIG_PPC64)
const unsigned char elf_ident[EI_NIDENT] = {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#else
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#endif
};
if (memcmp(mem, elf_ident, sizeof(elf_ident)) == 0)
return handle_elf_ppc64(mem, size);
#endif /* CONFIG_PPC64 */
pr_err("Unsupported Elf format detected\n");
return -1;
}
static int piegen(void)
{
struct stat st;
void *mem;
int fd;
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;
}
fout = fopen(opts.output_filename, "w");
if (fout == NULL) {
pr_perror("Can't open %s", opts.output_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(mem, st.st_size)) {
fclose(fout);
unlink(opts.output_filename);
goto err;
}
err:
fclose(fout);
printf("%s generated successfully.\n", opts.output_filename);
return 0;
}
int main(int argc, char *argv[])
{
const char *current_cflags = NULL;
int opt, idx, i;
char *action;
typedef struct {
const char *arch;
const char *cflags;
} compel_cflags_t;
static const compel_cflags_t compel_cflags[] = {
{
.arch = "x86",
.cflags = compel_cflags_pie,
}, {
.arch = "ia32",
.cflags = compel_cflags_nopic,
}, {
.arch = "aarch64",
.cflags = compel_cflags_pie,
}, {
.arch = "arm",
.cflags = compel_cflags_pie,
}, {
.arch = "ppc64",
.cflags = compel_cflags_pie,
},
};
static const char short_opts[] = "a:f:o:s:p:v:r:u:h";
static struct option long_opts[] = {
{ "arch", required_argument, 0, 'a' },
{ "file", required_argument, 0, 'f' },
{ "output", required_argument, 0, 'o' },
{ "stream", required_argument, 0, 's' },
{ "uapi-dir", required_argument, 0, 'u' },
{ "sym-prefix", required_argument, 0, 'p' },
{ "variable", required_argument, 0, 'v' },
{ "pcrelocs", required_argument, 0, 'r' },
{ "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 'a':
for (i = 0; i < ARRAY_SIZE(compel_cflags); i++) {
if (!strcmp(optarg, compel_cflags[i].arch)) {
current_cflags = compel_cflags[i].cflags;
break;
}
}
if (!current_cflags)
goto usage;
break;
case 'f':
opts.input_filename = optarg;
break;
case 'o':
opts.output_filename = optarg;
break;
case 'u':
opts.uapi_dir = optarg;
break;
case 's':
opts.stream_name = optarg;
break;
case 'p':
opts.prefix_name = optarg;
break;
case 'v':
opts.var_name = optarg;
break;
case 'r':
opts.nrgotpcrel_name = optarg;
break;
case 'h':
goto usage;
default:
break;
}
}
if (optind >= argc)
goto usage;
action = argv[optind++];
if (!strcmp(action, "cflags")) {
if (!current_cflags)
goto usage;
printf("%s", current_cflags);
return 0;
}
if (!strcmp(action, "ldflags")) {
printf("%s", compel_ldflags);
return 0;
}
if (!strcmp(action, "piegen")) {
if (!opts.input_filename)
goto usage;
return piegen();
}
usage:
printf("Usage:\n");
printf(" compel --arch=(x86|ia32|aarch64|arm|ppc64) cflags\n");
printf(" compel --arch=(x86|ia32|aarch64|arm|ppc64) ldflags\n");
printf(" compel -f filename piegen\n");
return 1;
}