blob: 0fb64b85bea0c59e3da192fa84b3778062567619 [file] [log] [blame]
/*
* Copyright 2015 Big Switch Networks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _GNU_SOURCE
#include <inttypes.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <elf.h>
#include <math.h>
#include "ubpf.h"
void ubpf_set_register_offset(int x);
static void *readfile(const char *path, size_t maxlen, size_t *len);
static void register_functions(struct ubpf_vm *vm);
static void usage(const char *name)
{
fprintf(stderr, "usage: %s [-h] [-j|--jit] [-m|--mem PATH] [-e|--elf] BINARY\n", name);
fprintf(stderr, "\nExecutes the eBPF code in BINARY and prints the result to stdout.\n");
fprintf(stderr, "If --mem is given then the specified file will be read and a pointer\nto its data passed in r1.\n");
fprintf(stderr, "If --jit is given then the JIT compiler will be used.\n");
fprintf(stderr, "\nOther options:\n");
fprintf(stderr, " -r, --register-offset NUM: Change the mapping from eBPF to x86 registers\n");
}
int main(int argc, char **argv)
{
struct option longopts[] = {
{ .name = "help", .val = 'h', },
{ .name = "mem", .val = 'm', .has_arg=1 },
{ .name = "jit", .val = 'm' },
{ .name = "register-offset", .val = 'r', .has_arg=1 },
};
const char *mem_filename = NULL;
bool jit = false;
int opt;
while ((opt = getopt_long(argc, argv, "hm:jer:", longopts, NULL)) != -1) {
switch (opt) {
case 'm':
mem_filename = optarg;
break;
case 'j':
jit = true;
break;
case 'r':
ubpf_set_register_offset(atoi(optarg));
break;
case 'h':
usage(argv[0]);
return 0;
default:
usage(argv[0]);
return 1;
}
}
if (argc != optind + 1) {
usage(argv[0]);
return 1;
}
const char *code_filename = argv[optind];
size_t code_len;
void *code = readfile(code_filename, 1024*1024, &code_len);
if (code == NULL) {
return 1;
}
size_t mem_len = 0;
void *mem = NULL;
if (mem_filename != NULL) {
mem = readfile(mem_filename, 1024*1024, &mem_len);
if (mem == NULL) {
return 1;
}
}
struct ubpf_vm *vm = ubpf_create();
if (!vm) {
fprintf(stderr, "Failed to create VM\n");
return 1;
}
register_functions(vm);
/*
* The ELF magic corresponds to an RSH instruction with an offset,
* which is invalid.
*/
bool elf = code_len >= SELFMAG && !memcmp(code, ELFMAG, SELFMAG);
char *errmsg;
int rv;
if (elf) {
rv = ubpf_load_elf(vm, code, code_len, &errmsg);
} else {
rv = ubpf_load(vm, code, code_len, &errmsg);
}
if (rv < 0) {
fprintf(stderr, "Failed to load code: %s\n", errmsg);
free(errmsg);
ubpf_destroy(vm);
return 1;
}
uint64_t ret;
if (jit) {
ubpf_jit_fn fn = ubpf_compile(vm, &errmsg);
if (fn == NULL) {
fprintf(stderr, "Failed to compile: %s\n", errmsg);
free(errmsg);
return 1;
}
ret = fn(mem, mem_len);
} else {
ret = ubpf_exec(vm, mem, mem_len);
}
printf("0x%"PRIx64"\n", ret);
ubpf_destroy(vm);
return 0;
}
static void *readfile(const char *path, size_t maxlen, size_t *len)
{
FILE *file;
if (!strcmp(path, "-")) {
file = fdopen(STDIN_FILENO, "r");
} else {
file = fopen(path, "r");
}
if (file == NULL) {
fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
return NULL;
}
void *data = calloc(maxlen, 1);
size_t offset = 0;
size_t rv;
while ((rv = fread(data+offset, 1, maxlen-offset, file)) > 0) {
offset += rv;
}
if (ferror(file)) {
fprintf(stderr, "Failed to read %s: %s\n", path, strerror(errno));
fclose(file);
free(data);
return NULL;
}
if (!feof(file)) {
fprintf(stderr, "Failed to read %s because it is too large (max %u bytes)\n",
path, (unsigned)maxlen);
fclose(file);
free(data);
return NULL;
}
fclose(file);
if (len) {
*len = offset;
}
return data;
}
static uint64_t
gather_bytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e)
{
return ((uint64_t)a << 32) |
((uint32_t)b << 24) |
((uint32_t)c << 16) |
((uint16_t)d << 8) |
e;
}
static void
trash_registers(void)
{
/* Overwrite all caller-save registers */
asm(
"mov $0xf0, %rax;"
"mov $0xf1, %rcx;"
"mov $0xf2, %rdx;"
"mov $0xf3, %rsi;"
"mov $0xf4, %rdi;"
"mov $0xf5, %r8;"
"mov $0xf6, %r9;"
"mov $0xf7, %r10;"
"mov $0xf8, %r11;"
);
}
static uint32_t
sqrti(uint32_t x)
{
return sqrt(x);
}
static void
register_functions(struct ubpf_vm *vm)
{
ubpf_register(vm, 0, "gather_bytes", gather_bytes);
ubpf_register(vm, 1, "memfrob", memfrob);
ubpf_register(vm, 2, "trash_registers", trash_registers);
ubpf_register(vm, 3, "sqrti", sqrti);
ubpf_register(vm, 4, "strcmp_ext", strcmp);
}