From 9a36c9226c4a237208a7735f0e6a6fd1eefb60ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 21 Jun 2016 15:10:10 -0700 Subject: [PATCH] Add support for BPF Signed-off-by: Richard Henderson --- ChangeLog | 4 + backends/ChangeLog | 10 + backends/Makefile.am | 15 +- backends/bpf_init.c | 60 ++++++ backends/bpf_regs.c | 64 +++++++ backends/bpf_reloc.def | 31 ++++ backends/common-reloc.c | 7 +- configure.ac | 4 + libasm/ChangeLog | 4 + libasm/disasm_cb.c | 4 +- libcpu/ChangeLog | 7 + libcpu/Makefile.am | 6 + libcpu/bpf_disasm.c | 288 +++++++++++++++++++++++++++++ libcpu/i386_disasm.c | 3 +- libebl/ChangeLog | 5 + libebl/ebl-hooks.h | 2 +- libebl/eblopenbackend.c | 1 + src/ChangeLog | 4 + src/elflint.c | 2 +- tests/ChangeLog | 9 + tests/Makefile.am | 7 +- tests/run-disasm-bpf.sh | 63 +++++++ tests/testfile-bpf-dis1.expect.bz2 | Bin 0 -> 1497 bytes tests/testfile-bpf-dis1.o.bz2 | Bin 0 -> 737 bytes 24 files changed, 590 insertions(+), 10 deletions(-) create mode 100644 backends/bpf_init.c create mode 100644 backends/bpf_regs.c create mode 100644 backends/bpf_reloc.def create mode 100644 libcpu/bpf_disasm.c create mode 100755 tests/run-disasm-bpf.sh create mode 100644 tests/testfile-bpf-dis1.expect.bz2 create mode 100644 tests/testfile-bpf-dis1.o.bz2 diff --git a/ChangeLog b/ChangeLog index 1a7d9ccf1..5be4c5ec7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2016-06-28 Richard Henderson + + * configure.ac (HAVE_LINUX_BPF_H): New test and conditional. + 2016-06-10 Mark Wielaard * CONTRIBUTING: Extend patch, committer and maintainer guidelines. diff --git a/backends/ChangeLog b/backends/ChangeLog index afda37a9c..7cd190658 100644 --- a/backends/ChangeLog +++ b/backends/ChangeLog @@ -1,3 +1,13 @@ +2016-06-28 Richard Henderson + + * Makefile.am (modules): Add bpf. + (libebl_pic): Add libebl_bpf_pic.a. + (am_libebl_bpf_pic_a_OBJECTS): New. + * bpf_init.c, bpf_regs.c, bpf_reloc.def: New files. + * common-reloc.c (copy_reloc_p): Honor NO_COPY_RELOC. + (init_reloc): Likewise. + + 2016-05-20 Andreas Schwab * Makefile.am (modules): Add m68k. diff --git a/backends/Makefile.am b/backends/Makefile.am index bf5239128..b553ec34c 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -33,12 +33,12 @@ AM_CPPFLAGS += -I$(top_srcdir)/libebl -I$(top_srcdir)/libasm \ modules = i386 sh x86_64 ia64 alpha arm aarch64 sparc ppc ppc64 s390 \ - tilegx m68k + tilegx m68k bpf libebl_pic = libebl_i386_pic.a libebl_sh_pic.a libebl_x86_64_pic.a \ libebl_ia64_pic.a libebl_alpha_pic.a libebl_arm_pic.a \ libebl_aarch64_pic.a libebl_sparc_pic.a libebl_ppc_pic.a \ libebl_ppc64_pic.a libebl_s390_pic.a libebl_tilegx_pic.a \ - libebl_m68k_pic.a + libebl_m68k_pic.a libebl_bpf_pic.a noinst_LIBRARIES = $(libebl_pic) noinst_DATA = $(libebl_pic:_pic.a=.so) @@ -118,6 +118,16 @@ m68k_SRCS = m68k_init.c m68k_symbol.c m68k_regs.c \ libebl_m68k_pic_a_SOURCES = $(m68k_SRCS) am_libebl_m68k_pic_a_OBJECTS = $(m68k_SRCS:.c=.os) +bpf_SRCS = bpf_init.c bpf_regs.c +# The disam hook depends on this if we have linux/bpf.h. +if HAVE_LINUX_BPF_H +cpu_bpf = ../libcpu/libcpu_bpf.a +else +cpu_bpf = +endif +libebl_bpf_pic_a_SOURCES = $(bpf_SRCS) +am_libebl_bpf_pic_a_OBJECTS = $(bpf_SRCS:.c=.os) + libebl_%.so libebl_%.map: libebl_%_pic.a $(libelf) $(libdw) @rm -f $(@:.so=.map) @@ -131,6 +141,7 @@ libebl_%.so libebl_%.map: libebl_%_pic.a $(libelf) $(libdw) libebl_i386.so: $(cpu_i386) libebl_x86_64.so: $(cpu_x86_64) +libebl_bpf.so: $(cpu_bpf) install: install-am install-ebl-modules install-ebl-modules: diff --git a/backends/bpf_init.c b/backends/bpf_init.c new file mode 100644 index 000000000..22842e26d --- /dev/null +++ b/backends/bpf_init.c @@ -0,0 +1,60 @@ +/* Initialization of BPF specific backend library. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * 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 + + or both in parallel, as here. + + elfutils 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define BACKEND bpf_ +#define RELOC_PREFIX R_BPF_ +#include "libebl_CPU.h" + +/* This defines the common reloc hooks based on bpf_reloc.def. */ +#define NO_RELATIVE_RELOC +#define NO_COPY_RELOC +#include "common-reloc.c" + + +const char * +bpf_init (Elf *elf __attribute__ ((unused)), + GElf_Half machine __attribute__ ((unused)), + Ebl *eh, size_t ehlen) +{ + /* Check whether the Elf_BH object has a sufficent size. */ + if (ehlen < sizeof (Ebl)) + return NULL; + + /* We handle it. */ + eh->name = "BPF"; + bpf_init_reloc (eh); + HOOK (eh, register_info); +#ifdef HAVE_LINUX_BPF_H + HOOK (eh, disasm); +#endif + + return MODVERSION; +} diff --git a/backends/bpf_regs.c b/backends/bpf_regs.c new file mode 100644 index 000000000..180af83b4 --- /dev/null +++ b/backends/bpf_regs.c @@ -0,0 +1,64 @@ +/* Register names and numbers for BPF DWARF. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * 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 + + or both in parallel, as here. + + elfutils 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#ifdef HAVE_LINUX_BPF_H +#include +#else +#define MAX_BPF_REG 10 +#endif + +#define BACKEND bpf_ +#include "libebl_CPU.h" + +ssize_t +bpf_register_info (Ebl *ebl __attribute__ ((unused)), + int regno, char *name, size_t namelen, + const char **prefix, const char **setname, + int *bits, int *type) +{ + ssize_t len; + + if (name == NULL) + return MAX_BPF_REG; + if (regno < 0 || regno >= MAX_BPF_REG) + return -1; + + *prefix = ""; + *setname = "integer"; + *bits = 64; + *type = DW_ATE_signed; + + len = snprintf(name, namelen, "r%d", regno); + return ((size_t)len < namelen ? len : -1); +} diff --git a/backends/bpf_reloc.def b/backends/bpf_reloc.def new file mode 100644 index 000000000..a410da979 --- /dev/null +++ b/backends/bpf_reloc.def @@ -0,0 +1,31 @@ +/* List the relocation types for BPF. -*- C -*- + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * 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 + + or both in parallel, as here. + + elfutils 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +/* NAME, REL|EXEC|DYN */ + +RELOC_TYPE (NONE, EXEC|DYN) +RELOC_TYPE (MAP_FD, REL|EXEC|DYN) diff --git a/backends/common-reloc.c b/backends/common-reloc.c index 3317b6c99..096ed1c73 100644 --- a/backends/common-reloc.c +++ b/backends/common-reloc.c @@ -124,12 +124,13 @@ EBLHOOK(reloc_valid_use) (Elf *elf, int reloc) return type > ET_NONE && type < ET_CORE && (uses & (1 << (type - 1))); } - +#ifndef NO_COPY_RELOC bool EBLHOOK(copy_reloc_p) (int reloc) { return reloc == R_TYPE (COPY); } +#endif bool EBLHOOK(none_reloc_p) (int reloc) @@ -151,8 +152,10 @@ EBLHOOK(init_reloc) (Ebl *ebl) ebl->reloc_type_name = EBLHOOK(reloc_type_name); ebl->reloc_type_check = EBLHOOK(reloc_type_check); ebl->reloc_valid_use = EBLHOOK(reloc_valid_use); - ebl->copy_reloc_p = EBLHOOK(copy_reloc_p); ebl->none_reloc_p = EBLHOOK(none_reloc_p); +#ifndef NO_COPY_RELOC + ebl->copy_reloc_p = EBLHOOK(copy_reloc_p); +#endif #ifndef NO_RELATIVE_RELOC ebl->relative_reloc_p = EBLHOOK(relative_reloc_p); #endif diff --git a/configure.ac b/configure.ac index 07c04637a..926715cfc 100644 --- a/configure.ac +++ b/configure.ac @@ -361,6 +361,10 @@ else fi AC_SUBST([argp_LDADD]) +dnl Check if we have for EM_BPF disassembly. +AC_CHECK_HEADERS(linux/bpf.h) +AM_CONDITIONAL(HAVE_LINUX_BPF_H, [test "x$ac_cv_header_linux_bpf_h" = "xyes"]) + dnl The directories with content. dnl Documentation. diff --git a/libasm/ChangeLog b/libasm/ChangeLog index a8ac2c7c3..afc7e8ecd 100644 --- a/libasm/ChangeLog +++ b/libasm/ChangeLog @@ -1,3 +1,7 @@ +2016-06-28 Richard Henderson + + * disasm_cb.c (disasm_cb): Pass ebl to disasm hook. + 2016-02-12 Mark Wielaard * asm_begin.c (prepare_text_output): Only call __fsetlocking when diff --git a/libasm/disasm_cb.c b/libasm/disasm_cb.c index eb3689c79..cf278c71f 100644 --- a/libasm/disasm_cb.c +++ b/libasm/disasm_cb.c @@ -173,7 +173,7 @@ disasm_cb (DisasmCtx_t *ctx, const uint8_t **startp, const uint8_t *end, getsym = default_elf_getsym; } - return ctx->ebl->disasm (startp, end, addr, fmt, outcb, getsym, outcbarg, - symcbarg); + return ctx->ebl->disasm (ctx->ebl, startp, end, addr, fmt, outcb, + getsym, outcbarg, symcbarg); } INTDEF (disasm_cb) diff --git a/libcpu/ChangeLog b/libcpu/ChangeLog index c953c7b38..269e777b8 100644 --- a/libcpu/ChangeLog +++ b/libcpu/ChangeLog @@ -1,3 +1,10 @@ +2016-06-28 Richard Henderson + + * Makefile.am (noinst_LIBRARIES): Add libcpu_bpf.a. + (libcpu_bpf_a_SOURCES, libcpu_bpf_a_CFLAGS): New. + * bpf_disasm.c: New file. + * i386_disasm.c (i386_disasm): Add ebl parameter. + 2015-10-05 Josh Stone * Makefile.am (%_defs): Add AM_V_GEN and AM_V_at silencers. diff --git a/libcpu/Makefile.am b/libcpu/Makefile.am index f0caaea6e..b98b58381 100644 --- a/libcpu/Makefile.am +++ b/libcpu/Makefile.am @@ -45,6 +45,12 @@ i386_gendis_SOURCES = i386_gendis.c i386_lex.l i386_parse.y i386_disasm.o: i386.mnemonics $(srcdir)/i386_dis.h x86_64_disasm.o: x86_64.mnemonics $(srcdir)/x86_64_dis.h +if HAVE_LINUX_BPF_H +noinst_LIBRARIES += libcpu_bpf.a +libcpu_bpf_a_SOURCES = bpf_disasm.c +libcpu_bpf_a_CFLAGS = $(AM_CFLAGS) -Wno-format-nonliteral +endif + %_defs: $(srcdir)/defs/i386 $(AM_V_GEN)m4 -D$* -DDISASSEMBLER $< > $@T $(AM_V_at)mv -f $@T $@ diff --git a/libcpu/bpf_disasm.c b/libcpu/bpf_disasm.c new file mode 100644 index 000000000..6301dccca --- /dev/null +++ b/libcpu/bpf_disasm.c @@ -0,0 +1,288 @@ +/* Disassembler for BPF. + Copyright (C) 2016 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * 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 + + or both in parallel, as here. + + elfutils 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "../libelf/common.h" +#include "../libebl/libeblP.h" + + +static const char class_string[8][8] = { + [BPF_LD] = "ld", + [BPF_LDX] = "ldx", + [BPF_ST] = "st", + [BPF_STX] = "stx", + [BPF_ALU] = "alu", + [BPF_JMP] = "jmp", + [BPF_RET] = "6", /* completely unused in ebpf */ + [BPF_ALU64] = "alu64", +}; + +/* Dest = 1$, Src = 2$, Imm = 3$, Off = 4$, Branch = 5$. */ + +#define DST "r%1$d" +#define DSTU "(u32)" DST +#define DSTS "(s64)" DST + +#define SRC "r%2$d" +#define SRCU "(u32)" SRC +#define SRCS "(s64)" SRC + +#define IMMS "%3$d" +#define IMMX "%3$#x" +#define OFF "%4$+d" +#define JMP "%5$#x" + +#define A32(O, S) DST " = " DSTU " " #O " " S +#define A64(O, S) DST " " #O "= " S +#define J64(D, O, S) "if " D " " #O " " S " goto " JMP +#define LOAD(T) DST " = *(" #T " *)(" SRC OFF ")" +#define STORE(T, S) "*(" #T " *)(" DST OFF ") = " S +#define XADD(T, S) "lock *(" #T " *)(" DST OFF ") += " S +#define LDSKB(T, S) "r0 = *(" #T " *)skb[" S "]" + +/* 8 character field between opcode and arguments. */ +static const char * const code_fmts[256] = { + [BPF_ALU | BPF_ADD | BPF_K] = A32(+, IMMS), + [BPF_ALU | BPF_SUB | BPF_K] = A32(-, IMMS), + [BPF_ALU | BPF_MUL | BPF_K] = A32(*, IMMS), + [BPF_ALU | BPF_DIV | BPF_K] = A32(/, IMMS), + [BPF_ALU | BPF_OR | BPF_K] = A32(|, IMMX), + [BPF_ALU | BPF_AND | BPF_K] = A32(&, IMMX), + [BPF_ALU | BPF_LSH | BPF_K] = A32(<<, IMMS), + [BPF_ALU | BPF_RSH | BPF_K] = A32(>>, IMMS), + [BPF_ALU | BPF_MOD | BPF_K] = A32(%, IMMS), + [BPF_ALU | BPF_XOR | BPF_K] = A32(^, IMMX), + [BPF_ALU | BPF_MOV | BPF_K] = DST " = " IMMX, + [BPF_ALU | BPF_ARSH | BPF_K] = DST " = (u32)((s32)" DST " >> " IMMS ")", + + [BPF_ALU | BPF_ADD | BPF_X] = A32(+, SRCU), + [BPF_ALU | BPF_SUB | BPF_X] = A32(-, SRCU), + [BPF_ALU | BPF_MUL | BPF_X] = A32(*, SRCU), + [BPF_ALU | BPF_DIV | BPF_X] = A32(/, SRCU), + [BPF_ALU | BPF_OR | BPF_X] = A32(|, SRCU), + [BPF_ALU | BPF_AND | BPF_X] = A32(&, SRCU), + [BPF_ALU | BPF_LSH | BPF_X] = A32(<<, SRCU), + [BPF_ALU | BPF_RSH | BPF_X] = A32(>>, SRCU), + [BPF_ALU | BPF_MOD | BPF_X] = A32(%, SRCU), + [BPF_ALU | BPF_XOR | BPF_X] = A32(^, SRCU), + [BPF_ALU | BPF_MOV | BPF_X] = DST " = " SRCU, + [BPF_ALU | BPF_ARSH | BPF_X] = DST " = (u32)((s32)" DST " >> " SRC ")", + + [BPF_ALU64 | BPF_ADD | BPF_K] = A64(+, IMMS), + [BPF_ALU64 | BPF_SUB | BPF_K] = A64(-, IMMS), + [BPF_ALU64 | BPF_MUL | BPF_K] = A64(*, IMMS), + [BPF_ALU64 | BPF_DIV | BPF_K] = A64(/, IMMS), + [BPF_ALU64 | BPF_OR | BPF_K] = A64(|, IMMS), + [BPF_ALU64 | BPF_AND | BPF_K] = A64(&, IMMS), + [BPF_ALU64 | BPF_LSH | BPF_K] = A64(<<, IMMS), + [BPF_ALU64 | BPF_RSH | BPF_K] = A64(>>, IMMS), + [BPF_ALU64 | BPF_MOD | BPF_K] = A64(%, IMMS), + [BPF_ALU64 | BPF_XOR | BPF_K] = A64(^, IMMS), + [BPF_ALU64 | BPF_MOV | BPF_K] = DST " = " IMMS, + [BPF_ALU64 | BPF_ARSH | BPF_K] = DST " = (s64)" DST " >> " IMMS, + + [BPF_ALU64 | BPF_ADD | BPF_X] = A64(+, SRC), + [BPF_ALU64 | BPF_SUB | BPF_X] = A64(-, SRC), + [BPF_ALU64 | BPF_MUL | BPF_X] = A64(*, SRC), + [BPF_ALU64 | BPF_DIV | BPF_X] = A64(/, SRC), + [BPF_ALU64 | BPF_OR | BPF_X] = A64(|, SRC), + [BPF_ALU64 | BPF_AND | BPF_X] = A64(&, SRC), + [BPF_ALU64 | BPF_LSH | BPF_X] = A64(<<, SRC), + [BPF_ALU64 | BPF_RSH | BPF_X] = A64(>>, SRC), + [BPF_ALU64 | BPF_MOD | BPF_X] = A64(%, SRC), + [BPF_ALU64 | BPF_XOR | BPF_X] = A64(^, SRC), + [BPF_ALU64 | BPF_MOV | BPF_X] = DST " = " SRC, + [BPF_ALU64 | BPF_ARSH | BPF_X] = DST " = (s64)" DST " >> " SRC, + + [BPF_ALU | BPF_NEG] = DST " = (u32)-" DST, + [BPF_ALU64 | BPF_NEG] = DST " = -" DST, + + /* The imm field contains {16,32,64}. */ + [BPF_ALU | BPF_END | BPF_TO_LE] = DST " = le%3$-6d(" DST ")", + [BPF_ALU | BPF_END | BPF_TO_BE] = DST " = be%3$-6d(" DST ")", + + [BPF_JMP | BPF_JEQ | BPF_K] = J64(DST, ==, IMMS), + [BPF_JMP | BPF_JGT | BPF_K] = J64(DST, >, IMMS), + [BPF_JMP | BPF_JGE | BPF_K] = J64(DST, >=, IMMS), + [BPF_JMP | BPF_JSET | BPF_K] = J64(DST, &, IMMS), + [BPF_JMP | BPF_JNE | BPF_K] = J64(DST, !=, IMMS), + [BPF_JMP | BPF_JSGT | BPF_K] = J64(DSTS, >, IMMS), + [BPF_JMP | BPF_JSGE | BPF_K] = J64(DSTS, >=, IMMS), + + [BPF_JMP | BPF_JEQ | BPF_X] = J64(DST, ==, SRC), + [BPF_JMP | BPF_JGT | BPF_X] = J64(DST, >, SRC), + [BPF_JMP | BPF_JGE | BPF_X] = J64(DST, >=, SRC), + [BPF_JMP | BPF_JSET | BPF_X] = J64(DST, &, SRC), + [BPF_JMP | BPF_JNE | BPF_X] = J64(DST, !=, SRC), + [BPF_JMP | BPF_JSGT | BPF_X] = J64(DSTS, >, SRCS), + [BPF_JMP | BPF_JSGE | BPF_X] = J64(DSTS, >=, SRCS), + + [BPF_JMP | BPF_JA] = "goto " JMP, + [BPF_JMP | BPF_CALL] = "call " IMMS, + [BPF_JMP | BPF_EXIT] = "exit", + + [BPF_LDX | BPF_MEM | BPF_B] = LOAD(u8), + [BPF_LDX | BPF_MEM | BPF_H] = LOAD(u16), + [BPF_LDX | BPF_MEM | BPF_W] = LOAD(u32), + [BPF_LDX | BPF_MEM | BPF_DW] = LOAD(u64), + + [BPF_STX | BPF_MEM | BPF_B] = STORE(u8, SRC), + [BPF_STX | BPF_MEM | BPF_H] = STORE(u16, SRC), + [BPF_STX | BPF_MEM | BPF_W] = STORE(u32, SRC), + [BPF_STX | BPF_MEM | BPF_DW] = STORE(u64, SRC), + + [BPF_STX | BPF_XADD | BPF_W] = XADD(u32, SRC), + [BPF_STX | BPF_XADD | BPF_DW] = XADD(u64, SRC), + + [BPF_ST | BPF_MEM | BPF_B] = STORE(u8, IMMS), + [BPF_ST | BPF_MEM | BPF_H] = STORE(u16, IMMS), + [BPF_ST | BPF_MEM | BPF_W] = STORE(u32, IMMS), + [BPF_ST | BPF_MEM | BPF_DW] = STORE(u64, IMMS), + + [BPF_LD | BPF_ABS | BPF_B] = LDSKB(u8, IMMS), + [BPF_LD | BPF_ABS | BPF_H] = LDSKB(u16, IMMS), + [BPF_LD | BPF_ABS | BPF_W] = LDSKB(u32, IMMS), + + [BPF_LD | BPF_IND | BPF_B] = LDSKB(u8, SRC "+" IMMS), + [BPF_LD | BPF_IND | BPF_H] = LDSKB(u16, SRC "+" IMMS), + [BPF_LD | BPF_IND | BPF_W] = LDSKB(u32, SRC "+" IMMS), +}; + +static void +bswap_bpf_insn (struct bpf_insn *p) +{ + /* Note that the dst_reg and src_reg fields are 4-bit bitfields. + That means these two nibbles are (typically) layed out in the + opposite order between big- and little-endian hosts. This is + not required by any standard, but does happen to be true for + at least ppc, s390, arm and mips as big-endian hosts. */ + int t = p->dst_reg; + p->dst_reg = p->src_reg; + p->src_reg = t; + + /* The other 2 and 4 byte fields are trivially converted. */ + CONVERT (p->off); + CONVERT (p->imm); +} + +int +bpf_disasm (Ebl *ebl, const uint8_t **startp, const uint8_t *end, + GElf_Addr addr, const char *fmt __attribute__((unused)), + DisasmOutputCB_t outcb, + DisasmGetSymCB_t symcb __attribute__((unused)), + void *outcbarg, + void *symcbarg __attribute__((unused))) +{ + const bool need_bswap = MY_ELFDATA != ebl->data; + const uint8_t *start = *startp; + char buf[128]; + int len, retval = 0; + + while (start + sizeof(struct bpf_insn) <= end) + { + struct bpf_insn i; + unsigned code, class, jmp; + const char *code_fmt; + + memcpy(&i, start, sizeof(struct bpf_insn)); + if (need_bswap) + bswap_bpf_insn (&i); + start += sizeof(struct bpf_insn); + addr += sizeof(struct bpf_insn); + + /* ??? We really should pass in CTX, so that we can detect + wrong endianness and do some swapping. */ + + code = i.code; + code_fmt = code_fmts[code]; + + if (code == (BPF_LD | BPF_IMM | BPF_DW)) + { + struct bpf_insn i2; + uint64_t imm64; + + if (start + sizeof(struct bpf_insn) > end) + { + start -= sizeof(struct bpf_insn); + *startp = start; + goto done; + } + memcpy(&i2, start, sizeof(struct bpf_insn)); + if (need_bswap) + bswap_bpf_insn (&i2); + start += sizeof(struct bpf_insn); + addr += sizeof(struct bpf_insn); + + imm64 = (uint32_t)i.imm | ((uint64_t)i2.imm << 32); + switch (i.src_reg) + { + case 0: + code_fmt = DST " = %2$#" PRIx64; + break; + case BPF_PSEUDO_MAP_FD: + code_fmt = DST " = map_fd(%2$#" PRIx64 ")"; + break; + default: + code_fmt = DST " = ld_pseudo(%3$d, %2$#" PRIx64 ")"; + break; + } + len = snprintf(buf, sizeof(buf), code_fmt, + i.dst_reg, imm64, i.src_reg); + } + else if (code_fmt != NULL) + { + jmp = addr + i.off * sizeof(struct bpf_insn); + len = snprintf(buf, sizeof(buf), code_fmt, i.dst_reg, i.src_reg, + i.imm, i.off, jmp); + } + else + { + class = BPF_CLASS(code); + len = snprintf(buf, sizeof(buf), "invalid class %s", + class_string[class]); + } + + *startp = start; + retval = outcb (buf, len, outcbarg); + if (retval != 0) + goto done; + } + + done: + return retval; +} diff --git a/libcpu/i386_disasm.c b/libcpu/i386_disasm.c index 832241f2c..ceb5164f0 100644 --- a/libcpu/i386_disasm.c +++ b/libcpu/i386_disasm.c @@ -313,7 +313,8 @@ struct output_data int -i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, +i386_disasm (Ebl *ebl __attribute__((unused)), + const uint8_t **startp, const uint8_t *end, GElf_Addr addr, const char *fmt, DisasmOutputCB_t outcb, DisasmGetSymCB_t symcb, void *outcbarg, void *symcbarg) { diff --git a/libebl/ChangeLog b/libebl/ChangeLog index 97a9b8972..8ff401062 100644 --- a/libebl/ChangeLog +++ b/libebl/ChangeLog @@ -1,3 +1,8 @@ +2016-06-28 Richard Henderson + + * ebl-hooks.h (EBLHOOK(disasm)): Add ebl parameter. + * eblopenbackend.c (machines): Add EM_BPF entry. + 2016-05-20 Andreas Schwab * eblopenbackend.c (machines) [EM_68K]: Set class and data. diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index 2e3144644..a7f47554f 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -150,7 +150,7 @@ int EBLHOOK(syscall_abi) (Ebl *ebl, int *sp, int *pc, int *callno, int args[6]); /* Disassembler function. */ -int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end, +int EBLHOOK(disasm) (Ebl *ebl, const uint8_t **startp, const uint8_t *end, GElf_Addr addr, const char *fmt, DisasmOutputCB_t outcb, DisasmGetSymCB_t symcb, void *outcbarg, void *symcbarg); diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c index 2b922543e..16ec1c458 100644 --- a/libebl/eblopenbackend.c +++ b/libebl/eblopenbackend.c @@ -132,6 +132,7 @@ static const struct { "arc", "elf_arc_a5", "arc_a5", 6, EM_ARC_A5, 0, 0 }, { "xtensa", "elf_xtensa", "xtensa", 6, EM_XTENSA, 0, 0 }, { "aarch64", "elf_aarch64", "aarch64", 7, EM_AARCH64, ELFCLASS64, 0 }, + { "bpf", "elf_bpf", "bpf", 3, EM_BPF, 0, 0 }, }; #define nmachines (sizeof (machines) / sizeof (machines[0])) diff --git a/src/ChangeLog b/src/ChangeLog index bdc9d1337..21fc7d517 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,7 @@ +2016-06-28 Richard Henderson + + * elflint.c (valid_e_machine): Add EM_BPF. + 2016-04-11 David Abdurachmanov * elfcmp.c (main): Fix self-comparison error with GCC 6. diff --git a/src/elflint.c b/src/elflint.c index 15b12f6f5..8c298c935 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -344,7 +344,7 @@ static const int valid_e_machine[] = EM_CRIS, EM_JAVELIN, EM_FIREPATH, EM_ZSP, EM_MMIX, EM_HUANY, EM_PRISM, EM_AVR, EM_FR30, EM_D10V, EM_D30V, EM_V850, EM_M32R, EM_MN10300, EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA, - EM_TILEGX, EM_TILEPRO, EM_AARCH64 + EM_TILEGX, EM_TILEPRO, EM_AARCH64, EM_BPF }; #define nvalid_e_machine \ (sizeof (valid_e_machine) / sizeof (valid_e_machine[0])) diff --git a/tests/ChangeLog b/tests/ChangeLog index bcc296f14..1504d4873 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,12 @@ +2016-06-28 Richard Henderson + + * Makefile.am (TESTS): Add run-disasm-bpf.sh, conditionally. + (EXTRA_DIST): Add run-disasm-bpf.sh, testfile-bpf-dis1.expect.bz2, + testfile-bpf-dis1.o.bz2 + (run-disasm-bpf.sh): New file. + (testfile-bpf-dis1.expect.bz2): New file. + (testfile-bpf-dis1.o.bz2): New file. + 2016-02-09 Mark Wielaard * testfile-s390x-hash-both.bz2: New testfile. diff --git a/tests/Makefile.am b/tests/Makefile.am index fedcb39d2..274356fb3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -148,6 +148,9 @@ endif if HAVE_LIBASM check_PROGRAMS += $(asm_TESTS) TESTS += $(asm_TESTS) +if HAVE_LINUX_BPF_H +TESTS += run-disasm-bpf.sh +endif endif EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ @@ -322,7 +325,9 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile-zgabi32.bz2 testfile-zgabi64.bz2 \ testfile-zgabi32be.bz2 testfile-zgabi64be.bz2 \ run-elfgetchdr.sh run-elfgetzdata.sh run-elfputzdata.sh \ - run-zstrptr.sh run-compress-test.sh + run-zstrptr.sh run-compress-test.sh \ + run-disasm-bpf.sh \ + testfile-bpf-dis1.expect.bz2 testfile-bpf-dis1.o.bz2 if USE_VALGRIND valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1' diff --git a/tests/run-disasm-bpf.sh b/tests/run-disasm-bpf.sh new file mode 100755 index 000000000..8ca89d53f --- /dev/null +++ b/tests/run-disasm-bpf.sh @@ -0,0 +1,63 @@ +#! /bin/sh +# Copyright (C) 2016 Red Hat, Inc. +# This file is part of elfutils. +# +# This file 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 3 of the License, or +# (at your option) any later version. +# +# elfutils 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 . + +. $srcdir/test-subr.sh + +# This test file is created with +# +# #include +# #include +# +# int main() +# { +# int i; +# +# printf("\t.text\n"); +# +# for (i = 0; i < 256; ++i) +# if (i == (BPF_LD | BPF_IMM | BPF_DW)) +# printf("\t.byte\t%d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n", i); +# else +# { +# int regs = 0; +# switch (BPF_CLASS(i)) +# { +# case BPF_ALU: +# case BPF_ALU64: +# if (BPF_SRC(i) == BPF_X +# && BPF_OP(i) != BPF_NEG +# && BPF_OP(i) != BPF_END) +# regs = 0x21; +# break; +# case BPF_LDX: +# case BPF_STX: +# regs = 0x21; +# break; +# } +# printf("\t.byte\t%d, %d, 0, 0, 0, 0, 0, 0\n", i, regs); +# } +# +# return 0; +# } +# +# $ ./a.out | as -o z1.o +# $ objcopy -j .text z1.o z2.o +# +# Then emacs hexl edit e_machine to 0xf7. + +testfiles testfile-bpf-dis1.o testfile-bpf-dis1.expect +testrun_compare ${abs_top_builddir}/src/objdump -d testfile-bpf-dis1.o < testfile-bpf-dis1.expect diff --git a/tests/testfile-bpf-dis1.expect.bz2 b/tests/testfile-bpf-dis1.expect.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..b4a778e03882796d5c0682e2ac46daf8ecf4f53c GIT binary patch literal 1497 zc-nnbX;2eJ7~LcsAz+#yC~`O?

dIQ^E~S5`oGQ&L9R#w17a8CL9WK$)SRgK}Rr= zqZtJ`6DMfVAmvawii}_Y#Y8UAQa}hTMM@*!(O~=6zS$pdcD}dY?t9-XB{I>KK&D%T z(f81P`W={*AIZ-^C1ih8X#k*m|NczeQMl3mty3d`f>o7R>ey=JF|}ufXpGMa8y>5r zI`w!51q0Xta`gx+OH6foCz|uz6rHD%03ZS=KCecK$BmX&1yt<(5~8J3t94CW%yJej zdL_O;$gBp5#)(9TenVl<*m}^q&l8_s+PY^D^MDTv0F)@8gg;udL|liN1E4jQVvNON zUEEZv%=WgPV$YMmbzGlHuI;kM3F+DBfU_Mfo3;=NV zaA=$lwCM-dUIPG}?^VBD+u(AJpzewPbPi*EGWfHdTsrEhb0)h&X9*t z2J1BKH95Bs%-5kToUM(F9^5j6gy{_g&{U4j5o9h51StA4; zXtFl_dTH1rb@`1_QLe|Qrutfl8|w&Xp#Ftu^hVWU2h}C{Rkbm*p~@?HqtDCBYvtaS zdgX)9BiBYj#V7fNN${-mXjsRDTqHW9qA!yT|57p4{f8W&9P>R?TrPXoa zf~$-GPo~2uGC$r~D;K#yvXcEm)iuL7YL0;2x;Plic3*~KkscUi+NHC$EeWo&kkZ5__q;@AAaLP` z8d?H7QZ8-yPfg}{r_VilKI8MYe;0d@Vb2IO>chY(J9FP}-3?$iRH&O%P-=e)LoT?o z{X+TmIEtHP6U<*s8MFqX_Lui%q?uIzQ z?%U@O^3I0%%)SG|JxN9F${Pf)LEEO}UZ}5Ux1pw@cVl@nB4g6uzr$(t_S9;#N8747 z)YqcSGC2Do;6ZiMNbY0cn*oK#+7;>5tbmWF&EZy?%tk7m2g-acu5$qPkb3G4^VArN zk*SU918Ozt4W2{X2+3B#GAn2Nqo-RtlTJ~PlGcsIp{3$g^EGM3nsc_$VsOdMnkX)7u*31|Ys;$nnvdWU^`o%6)8eb#XFs6lMH0Uz lG40xTxMh%*`Xd15B(PU3o)do8lK0D0X`q3HR=908=^v$#g8u*j literal 0 Hc-jL100001 diff --git a/tests/testfile-bpf-dis1.o.bz2 b/tests/testfile-bpf-dis1.o.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..94bb612bcbfb191519c42b7b8c45981df0566f9b GIT binary patch literal 737 zc-nJ{%CIzaj8qGb++%U#9|Lp5e}douUfH8mf>iHjK+W-u_SF>p2jX*|H>tHzR#f{UHx7GF6i zm6UOEv9F;m!%Tx2YCbbEd}atUD6=H+x(TbPsrAfoGnG(W#+2EVpyoC!jDBBEmA5>m2c%ayO9s-~`?rAxP7{U)Yn<`!1AY}>W(;_Bw^;pHoK-1sS} zY2UJB%aJoz?jI|EYVY`Y;i4r=m#l-H8Db3mP<#NN`r4BD%GKGjS2%fm`Jl^W%%$e!W zWGAJZ64ID#l(J;8)eK2BPyHI7sV_Ua)UO&iHK{1?n6l%{nbUe|sd7f1lUgrtP`)~S z_d(t~KekS;bm8TGlTSE2Fe*QI;Dw8k3hR@yurvpT*-=c^Teodz6VN?+_CogV1K+nF z(m(QmOUxjT|28+j@cZuGAIDFeJazg{>!r)Qf395RQ;v@DJFqaBt?#D%-&?ou{LlM; zB46A6I|6{!Dx7wmSmHS9I1jA1UO@d&h8saRRe~Kqq^FP~kxi21$k% g2A&2d1~vzt1`wN9Nu5RS$L>j*hgw=69&`Ty0IB~rGynhq literal 0 Hc-jL100001 -- 2.47.3