Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
FEATURE-DUMP.bpf
feature
bpf_asm
bpf_dbg
bpf_exp.yacc.*
bpf_jit_disasm
+123
View File
@@ -0,0 +1,123 @@
# SPDX-License-Identifier: GPL-2.0
include ../scripts/Makefile.include
prefix ?= /usr/local
LEX = flex
YACC = bison
MAKE = make
INSTALL ?= install
CFLAGS += -Wall -O2
CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi \
-I$(srctree)/tools/include
CFLAGS += $(EXTRA_CFLAGS)
# This will work when bpf is built in tools env. where srctree
# isn't set and when invoked from selftests build, where srctree
# is set to ".". building_out_of_srctree is undefined for in srctree
# builds
ifeq ($(srctree),)
update_srctree := 1
endif
ifndef building_out_of_srctree
update_srctree := 1
endif
ifeq ($(update_srctree),1)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
FEATURE_USER = .bpf
FEATURE_TESTS = libbfd disassembler-four-args disassembler-init-styled
FEATURE_DISPLAY = libbfd
check_feat := 1
NON_CHECK_FEAT_TARGETS := clean bpftool_clean resolve_btfids_clean
ifdef MAKECMDGOALS
ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
check_feat := 0
endif
endif
ifeq ($(check_feat),1)
ifeq ($(FEATURES_DUMP),)
include $(srctree)/tools/build/Makefile.feature
else
include $(FEATURES_DUMP)
endif
endif
ifeq ($(feature-disassembler-four-args), 1)
CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
endif
ifeq ($(feature-disassembler-init-styled), 1)
CFLAGS += -DDISASM_INIT_STYLED
endif
$(OUTPUT)%.yacc.c: $(srctree)/tools/bpf/%.y
$(QUIET_BISON)$(YACC) -o $@ -d $<
$(OUTPUT)%.lex.c: $(srctree)/tools/bpf/%.l
$(QUIET_FLEX)$(LEX) -o $@ $<
$(OUTPUT)%.o: $(srctree)/tools/bpf/%.c
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
$(OUTPUT)%.yacc.o: $(OUTPUT)%.yacc.c
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
$(OUTPUT)%.lex.o: $(OUTPUT)%.lex.c
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
PROGS = $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg $(OUTPUT)bpf_asm
all: $(PROGS) bpftool
$(OUTPUT)bpf_jit_disasm: CFLAGS += -DPACKAGE='bpf_jit_disasm'
$(OUTPUT)bpf_jit_disasm: $(OUTPUT)bpf_jit_disasm.o
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ -lopcodes -lbfd -ldl
$(OUTPUT)bpf_dbg: $(OUTPUT)bpf_dbg.o
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ -lreadline
$(OUTPUT)bpf_asm: $(OUTPUT)bpf_asm.o $(OUTPUT)bpf_exp.yacc.o $(OUTPUT)bpf_exp.lex.o
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^
$(OUTPUT)bpf_exp.lex.c: $(OUTPUT)bpf_exp.yacc.c
$(OUTPUT)bpf_exp.yacc.o: $(OUTPUT)bpf_exp.yacc.c
$(OUTPUT)bpf_exp.lex.o: $(OUTPUT)bpf_exp.lex.c
clean: bpftool_clean resolve_btfids_clean
$(call QUIET_CLEAN, bpf-progs)
$(Q)$(RM) -r -- $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \
$(OUTPUT)bpf_asm $(OUTPUT)bpf_exp.yacc.* $(OUTPUT)bpf_exp.lex.*
$(call QUIET_CLEAN, core-gen)
$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpf
$(Q)$(RM) -r -- $(OUTPUT)feature
install: $(PROGS) bpftool_install
$(call QUIET_INSTALL, bpf_jit_disasm)
$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/bin
$(Q)$(INSTALL) $(OUTPUT)bpf_jit_disasm $(DESTDIR)$(prefix)/bin/bpf_jit_disasm
$(call QUIET_INSTALL, bpf_dbg)
$(Q)$(INSTALL) $(OUTPUT)bpf_dbg $(DESTDIR)$(prefix)/bin/bpf_dbg
$(call QUIET_INSTALL, bpf_asm)
$(Q)$(INSTALL) $(OUTPUT)bpf_asm $(DESTDIR)$(prefix)/bin/bpf_asm
bpftool:
$(call descend,bpftool)
bpftool_install:
$(call descend,bpftool,install)
bpftool_clean:
$(call descend,bpftool,clean)
resolve_btfids:
$(call descend,resolve_btfids)
resolve_btfids_clean:
$(call descend,resolve_btfids,clean)
.PHONY: all install clean bpftool bpftool_install bpftool_clean \
resolve_btfids resolve_btfids_clean
+52
View File
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Minimal BPF assembler
*
* Instead of libpcap high-level filter expressions, it can be quite
* useful to define filters in low-level BPF assembler (that is kept
* close to Steven McCanne and Van Jacobson's original BPF paper).
* In particular for BPF JIT implementors, JIT security auditors, or
* just for defining BPF expressions that contain extensions which are
* not supported by compilers.
*
* How to get into it:
*
* 1) read Documentation/networking/filter.rst
* 2) Run `bpf_asm [-c] <filter-prog file>` to translate into binary
* blob that is loadable with xt_bpf, cls_bpf et al. Note: -c will
* pretty print a C-like construct.
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
extern void bpf_asm_compile(FILE *fp, bool cstyle);
int main(int argc, char **argv)
{
FILE *fp = stdin;
bool cstyle = false;
int i;
for (i = 1; i < argc; i++) {
if (!strncmp("-c", argv[i], 2)) {
cstyle = true;
continue;
}
fp = fopen(argv[i], "r");
if (!fp) {
fp = stdin;
continue;
}
break;
}
bpf_asm_compile(fp, cstyle);
return 0;
}
File diff suppressed because it is too large Load Diff
+198
View File
@@ -0,0 +1,198 @@
/*
* BPF asm code lexer
*
* This program is free software; you can distribute it and/or modify
* it under the terms of 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.
*
* Syntax kept close to:
*
* Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new
* architecture for user-level packet capture. In Proceedings of the
* USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993
* Conference Proceedings (USENIX'93). USENIX Association, Berkeley,
* CA, USA, 2-2.
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
%{
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <linux/filter.h>
#include "bpf_exp.yacc.h"
extern void yyerror(const char *str);
%}
%option align
%option ecs
%option nounput
%option noreject
%option noinput
%option noyywrap
%option 8bit
%option caseless
%option yylineno
%%
"ldb" { return OP_LDB; }
"ldh" { return OP_LDH; }
"ld" { return OP_LD; }
"ldi" { return OP_LDI; }
"ldx" { return OP_LDX; }
"ldxi" { return OP_LDXI; }
"ldxb" { return OP_LDXB; }
"st" { return OP_ST; }
"stx" { return OP_STX; }
"jmp" { return OP_JMP; }
"ja" { return OP_JMP; }
"jeq" { return OP_JEQ; }
"jneq" { return OP_JNEQ; }
"jne" { return OP_JNEQ; }
"jlt" { return OP_JLT; }
"jle" { return OP_JLE; }
"jgt" { return OP_JGT; }
"jge" { return OP_JGE; }
"jset" { return OP_JSET; }
"add" { return OP_ADD; }
"sub" { return OP_SUB; }
"mul" { return OP_MUL; }
"div" { return OP_DIV; }
"mod" { return OP_MOD; }
"neg" { return OP_NEG; }
"and" { return OP_AND; }
"xor" { return OP_XOR; }
"or" { return OP_OR; }
"lsh" { return OP_LSH; }
"rsh" { return OP_RSH; }
"ret" { return OP_RET; }
"tax" { return OP_TAX; }
"txa" { return OP_TXA; }
"#"?("len") { return K_PKT_LEN; }
"#"?("proto") {
yylval.number = SKF_AD_PROTOCOL;
return extension;
}
"#"?("type") {
yylval.number = SKF_AD_PKTTYPE;
return extension;
}
"#"?("poff") {
yylval.number = SKF_AD_PAY_OFFSET;
return extension;
}
"#"?("ifidx") {
yylval.number = SKF_AD_IFINDEX;
return extension;
}
"#"?("nla") {
yylval.number = SKF_AD_NLATTR;
return extension;
}
"#"?("nlan") {
yylval.number = SKF_AD_NLATTR_NEST;
return extension;
}
"#"?("mark") {
yylval.number = SKF_AD_MARK;
return extension;
}
"#"?("queue") {
yylval.number = SKF_AD_QUEUE;
return extension;
}
"#"?("hatype") {
yylval.number = SKF_AD_HATYPE;
return extension;
}
"#"?("rxhash") {
yylval.number = SKF_AD_RXHASH;
return extension;
}
"#"?("cpu") {
yylval.number = SKF_AD_CPU;
return extension;
}
"#"?("vlan_tci") {
yylval.number = SKF_AD_VLAN_TAG;
return extension;
}
"#"?("vlan_pr") {
yylval.number = SKF_AD_VLAN_TAG_PRESENT;
return extension;
}
"#"?("vlan_avail") {
yylval.number = SKF_AD_VLAN_TAG_PRESENT;
return extension;
}
"#"?("vlan_tpid") {
yylval.number = SKF_AD_VLAN_TPID;
return extension;
}
"#"?("rand") {
yylval.number = SKF_AD_RANDOM;
return extension;
}
":" { return ':'; }
"," { return ','; }
"#" { return '#'; }
"%" { return '%'; }
"[" { return '['; }
"]" { return ']'; }
"(" { return '('; }
")" { return ')'; }
"x" { return 'x'; }
"a" { return 'a'; }
"+" { return '+'; }
"M" { return 'M'; }
"*" { return '*'; }
"&" { return '&'; }
([0][x][a-fA-F0-9]+) {
yylval.number = strtoul(yytext, NULL, 16);
return number;
}
([0][b][0-1]+) {
yylval.number = strtol(yytext + 2, NULL, 2);
return number;
}
(([0])|([-+]?[1-9][0-9]*)) {
yylval.number = strtol(yytext, NULL, 10);
return number;
}
([0][0-7]+) {
yylval.number = strtol(yytext + 1, NULL, 8);
return number;
}
[a-zA-Z_][a-zA-Z0-9_]+ {
yylval.label = strdup(yytext);
return label;
}
"/*"([^\*]|\*[^/])*"*/" { /* NOP */ }
";"[^\n]* { /* NOP */ }
^#.* { /* NOP */ }
[ \t]+ { /* NOP */ }
[ \n]+ { /* NOP */ }
. {
printf("unknown character \'%s\'", yytext);
yyerror("lex unknown character");
}
%%
+668
View File
@@ -0,0 +1,668 @@
/*
* BPF asm code parser
*
* This program is free software; you can distribute it and/or modify
* it under the terms of 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.
*
* Syntax kept close to:
*
* Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new
* architecture for user-level packet capture. In Proceedings of the
* USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993
* Conference Proceedings (USENIX'93). USENIX Association, Berkeley,
* CA, USA, 2-2.
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
%{
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <linux/filter.h>
#include "bpf_exp.yacc.h"
enum jmp_type { JTL, JFL, JKL };
extern FILE *yyin;
extern int yylineno;
extern int yylex(void);
extern void yyerror(const char *str);
extern void bpf_asm_compile(FILE *fp, bool cstyle);
static void bpf_set_curr_instr(uint16_t op, uint8_t jt, uint8_t jf, uint32_t k);
static void bpf_set_curr_label(char *label);
static void bpf_set_jmp_label(char *label, enum jmp_type type);
%}
%union {
char *label;
uint32_t number;
}
%token OP_LDB OP_LDH OP_LD OP_LDX OP_ST OP_STX OP_JMP OP_JEQ OP_JGT OP_JGE
%token OP_JSET OP_ADD OP_SUB OP_MUL OP_DIV OP_AND OP_OR OP_XOR OP_LSH OP_RSH
%token OP_RET OP_TAX OP_TXA OP_LDXB OP_MOD OP_NEG OP_JNEQ OP_JLT OP_JLE OP_LDI
%token OP_LDXI
%token K_PKT_LEN
%token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
%token extension number label
%type <label> label
%type <number> extension
%type <number> number
%%
prog
: line
| prog line
;
line
: instr
| labelled_instr
;
labelled_instr
: labelled instr
;
instr
: ldb
| ldh
| ld
| ldi
| ldx
| ldxi
| st
| stx
| jmp
| jeq
| jneq
| jlt
| jle
| jgt
| jge
| jset
| add
| sub
| mul
| div
| mod
| neg
| and
| or
| xor
| lsh
| rsh
| ret
| tax
| txa
;
labelled
: label ':' { bpf_set_curr_label($1); }
;
ldb
: OP_LDB '[' 'x' '+' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_B | BPF_IND, 0, 0, $5); }
| OP_LDB '[' '%' 'x' '+' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_B | BPF_IND, 0, 0, $6); }
| OP_LDB '[' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, $3); }
| OP_LDB extension {
bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
SKF_AD_OFF + $2); }
;
ldh
: OP_LDH '[' 'x' '+' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_H | BPF_IND, 0, 0, $5); }
| OP_LDH '[' '%' 'x' '+' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_H | BPF_IND, 0, 0, $6); }
| OP_LDH '[' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, $3); }
| OP_LDH extension {
bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
SKF_AD_OFF + $2); }
;
ldi
: OP_LDI '#' number {
bpf_set_curr_instr(BPF_LD | BPF_IMM, 0, 0, $3); }
| OP_LDI number {
bpf_set_curr_instr(BPF_LD | BPF_IMM, 0, 0, $2); }
;
ld
: OP_LD '#' number {
bpf_set_curr_instr(BPF_LD | BPF_IMM, 0, 0, $3); }
| OP_LD K_PKT_LEN {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_LEN, 0, 0, 0); }
| OP_LD extension {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
SKF_AD_OFF + $2); }
| OP_LD 'M' '[' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); }
| OP_LD '[' 'x' '+' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_IND, 0, 0, $5); }
| OP_LD '[' '%' 'x' '+' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_IND, 0, 0, $6); }
| OP_LD '[' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, $3); }
;
ldxi
: OP_LDXI '#' number {
bpf_set_curr_instr(BPF_LDX | BPF_IMM, 0, 0, $3); }
| OP_LDXI number {
bpf_set_curr_instr(BPF_LDX | BPF_IMM, 0, 0, $2); }
;
ldx
: OP_LDX '#' number {
bpf_set_curr_instr(BPF_LDX | BPF_IMM, 0, 0, $3); }
| OP_LDX K_PKT_LEN {
bpf_set_curr_instr(BPF_LDX | BPF_W | BPF_LEN, 0, 0, 0); }
| OP_LDX 'M' '[' number ']' {
bpf_set_curr_instr(BPF_LDX | BPF_MEM, 0, 0, $4); }
| OP_LDXB number '*' '(' '[' number ']' '&' number ')' {
if ($2 != 4 || $9 != 0xf) {
fprintf(stderr, "ldxb offset not supported!\n");
exit(1);
} else {
bpf_set_curr_instr(BPF_LDX | BPF_MSH | BPF_B, 0, 0, $6); } }
| OP_LDX number '*' '(' '[' number ']' '&' number ')' {
if ($2 != 4 || $9 != 0xf) {
fprintf(stderr, "ldxb offset not supported!\n");
exit(1);
} else {
bpf_set_curr_instr(BPF_LDX | BPF_MSH | BPF_B, 0, 0, $6); } }
;
st
: OP_ST 'M' '[' number ']' {
bpf_set_curr_instr(BPF_ST, 0, 0, $4); }
;
stx
: OP_STX 'M' '[' number ']' {
bpf_set_curr_instr(BPF_STX, 0, 0, $4); }
;
jmp
: OP_JMP label {
bpf_set_jmp_label($2, JKL);
bpf_set_curr_instr(BPF_JMP | BPF_JA, 0, 0, 0); }
;
jeq
: OP_JEQ '#' number ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, $3); }
| OP_JEQ 'x' ',' label ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_jmp_label($6, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); }
| OP_JEQ '%' 'x' ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); }
| OP_JEQ '#' number ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, $3); }
| OP_JEQ 'x' ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); }
| OP_JEQ '%' 'x' ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); }
;
jneq
: OP_JNEQ '#' number ',' label {
bpf_set_jmp_label($5, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, $3); }
| OP_JNEQ 'x' ',' label {
bpf_set_jmp_label($4, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); }
| OP_JNEQ '%' 'x' ',' label {
bpf_set_jmp_label($5, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); }
;
jlt
: OP_JLT '#' number ',' label {
bpf_set_jmp_label($5, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_K, 0, 0, $3); }
| OP_JLT 'x' ',' label {
bpf_set_jmp_label($4, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); }
| OP_JLT '%' 'x' ',' label {
bpf_set_jmp_label($5, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); }
;
jle
: OP_JLE '#' number ',' label {
bpf_set_jmp_label($5, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_K, 0, 0, $3); }
| OP_JLE 'x' ',' label {
bpf_set_jmp_label($4, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); }
| OP_JLE '%' 'x' ',' label {
bpf_set_jmp_label($5, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); }
;
jgt
: OP_JGT '#' number ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_K, 0, 0, $3); }
| OP_JGT 'x' ',' label ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_jmp_label($6, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); }
| OP_JGT '%' 'x' ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); }
| OP_JGT '#' number ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_K, 0, 0, $3); }
| OP_JGT 'x' ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); }
| OP_JGT '%' 'x' ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); }
;
jge
: OP_JGE '#' number ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_K, 0, 0, $3); }
| OP_JGE 'x' ',' label ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_jmp_label($6, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); }
| OP_JGE '%' 'x' ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); }
| OP_JGE '#' number ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_K, 0, 0, $3); }
| OP_JGE 'x' ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); }
| OP_JGE '%' 'x' ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); }
;
jset
: OP_JSET '#' number ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_K, 0, 0, $3); }
| OP_JSET 'x' ',' label ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_jmp_label($6, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); }
| OP_JSET '%' 'x' ',' label ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_jmp_label($7, JFL);
bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); }
| OP_JSET '#' number ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_K, 0, 0, $3); }
| OP_JSET 'x' ',' label {
bpf_set_jmp_label($4, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); }
| OP_JSET '%' 'x' ',' label {
bpf_set_jmp_label($5, JTL);
bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); }
;
add
: OP_ADD '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_ADD | BPF_K, 0, 0, $3); }
| OP_ADD 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_ADD | BPF_X, 0, 0, 0); }
| OP_ADD '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_ADD | BPF_X, 0, 0, 0); }
;
sub
: OP_SUB '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_SUB | BPF_K, 0, 0, $3); }
| OP_SUB 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_SUB | BPF_X, 0, 0, 0); }
| OP_SUB '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_SUB | BPF_X, 0, 0, 0); }
;
mul
: OP_MUL '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_MUL | BPF_K, 0, 0, $3); }
| OP_MUL 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_MUL | BPF_X, 0, 0, 0); }
| OP_MUL '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_MUL | BPF_X, 0, 0, 0); }
;
div
: OP_DIV '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_DIV | BPF_K, 0, 0, $3); }
| OP_DIV 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_DIV | BPF_X, 0, 0, 0); }
| OP_DIV '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_DIV | BPF_X, 0, 0, 0); }
;
mod
: OP_MOD '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_MOD | BPF_K, 0, 0, $3); }
| OP_MOD 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_MOD | BPF_X, 0, 0, 0); }
| OP_MOD '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_MOD | BPF_X, 0, 0, 0); }
;
neg
: OP_NEG {
bpf_set_curr_instr(BPF_ALU | BPF_NEG, 0, 0, 0); }
;
and
: OP_AND '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_AND | BPF_K, 0, 0, $3); }
| OP_AND 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_AND | BPF_X, 0, 0, 0); }
| OP_AND '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_AND | BPF_X, 0, 0, 0); }
;
or
: OP_OR '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_OR | BPF_K, 0, 0, $3); }
| OP_OR 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_OR | BPF_X, 0, 0, 0); }
| OP_OR '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_OR | BPF_X, 0, 0, 0); }
;
xor
: OP_XOR '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_XOR | BPF_K, 0, 0, $3); }
| OP_XOR 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_XOR | BPF_X, 0, 0, 0); }
| OP_XOR '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_XOR | BPF_X, 0, 0, 0); }
;
lsh
: OP_LSH '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_LSH | BPF_K, 0, 0, $3); }
| OP_LSH 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_LSH | BPF_X, 0, 0, 0); }
| OP_LSH '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_LSH | BPF_X, 0, 0, 0); }
;
rsh
: OP_RSH '#' number {
bpf_set_curr_instr(BPF_ALU | BPF_RSH | BPF_K, 0, 0, $3); }
| OP_RSH 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_RSH | BPF_X, 0, 0, 0); }
| OP_RSH '%' 'x' {
bpf_set_curr_instr(BPF_ALU | BPF_RSH | BPF_X, 0, 0, 0); }
;
ret
: OP_RET 'a' {
bpf_set_curr_instr(BPF_RET | BPF_A, 0, 0, 0); }
| OP_RET '%' 'a' {
bpf_set_curr_instr(BPF_RET | BPF_A, 0, 0, 0); }
| OP_RET 'x' {
bpf_set_curr_instr(BPF_RET | BPF_X, 0, 0, 0); }
| OP_RET '%' 'x' {
bpf_set_curr_instr(BPF_RET | BPF_X, 0, 0, 0); }
| OP_RET '#' number {
bpf_set_curr_instr(BPF_RET | BPF_K, 0, 0, $3); }
;
tax
: OP_TAX {
bpf_set_curr_instr(BPF_MISC | BPF_TAX, 0, 0, 0); }
;
txa
: OP_TXA {
bpf_set_curr_instr(BPF_MISC | BPF_TXA, 0, 0, 0); }
;
%%
static int curr_instr = 0;
static struct sock_filter out[BPF_MAXINSNS];
static char **labels, **labels_jt, **labels_jf, **labels_k;
static void bpf_assert_max(void)
{
if (curr_instr >= BPF_MAXINSNS) {
fprintf(stderr, "only max %u insns allowed!\n", BPF_MAXINSNS);
exit(1);
}
}
static void bpf_set_curr_instr(uint16_t code, uint8_t jt, uint8_t jf,
uint32_t k)
{
bpf_assert_max();
out[curr_instr].code = code;
out[curr_instr].jt = jt;
out[curr_instr].jf = jf;
out[curr_instr].k = k;
curr_instr++;
}
static void bpf_set_curr_label(char *label)
{
bpf_assert_max();
labels[curr_instr] = label;
}
static void bpf_set_jmp_label(char *label, enum jmp_type type)
{
bpf_assert_max();
switch (type) {
case JTL:
labels_jt[curr_instr] = label;
break;
case JFL:
labels_jf[curr_instr] = label;
break;
case JKL:
labels_k[curr_instr] = label;
break;
}
}
static int bpf_find_insns_offset(const char *label)
{
int i, max = curr_instr, ret = -ENOENT;
for (i = 0; i < max; i++) {
if (labels[i] && !strcmp(label, labels[i])) {
ret = i;
break;
}
}
if (ret == -ENOENT) {
fprintf(stderr, "no such label \'%s\'!\n", label);
exit(1);
}
return ret;
}
static void bpf_stage_1_insert_insns(void)
{
yyparse();
}
static void bpf_reduce_k_jumps(void)
{
int i;
for (i = 0; i < curr_instr; i++) {
if (labels_k[i]) {
int off = bpf_find_insns_offset(labels_k[i]);
out[i].k = (uint32_t) (off - i - 1);
}
}
}
static uint8_t bpf_encode_jt_jf_offset(int off, int i)
{
int delta = off - i - 1;
if (delta < 0 || delta > 255) {
fprintf(stderr, "error: insn #%d jumps to insn #%d, "
"which is out of range\n", i, off);
exit(1);
}
return (uint8_t) delta;
}
static void bpf_reduce_jt_jumps(void)
{
int i;
for (i = 0; i < curr_instr; i++) {
if (labels_jt[i]) {
int off = bpf_find_insns_offset(labels_jt[i]);
out[i].jt = bpf_encode_jt_jf_offset(off, i);
}
}
}
static void bpf_reduce_jf_jumps(void)
{
int i;
for (i = 0; i < curr_instr; i++) {
if (labels_jf[i]) {
int off = bpf_find_insns_offset(labels_jf[i]);
out[i].jf = bpf_encode_jt_jf_offset(off, i);
}
}
}
static void bpf_stage_2_reduce_labels(void)
{
bpf_reduce_k_jumps();
bpf_reduce_jt_jumps();
bpf_reduce_jf_jumps();
}
static void bpf_pretty_print_c(void)
{
int i;
for (i = 0; i < curr_instr; i++)
printf("{ %#04x, %2u, %2u, %#010x },\n", out[i].code,
out[i].jt, out[i].jf, out[i].k);
}
static void bpf_pretty_print(void)
{
int i;
printf("%u,", curr_instr);
for (i = 0; i < curr_instr; i++)
printf("%u %u %u %u,", out[i].code,
out[i].jt, out[i].jf, out[i].k);
printf("\n");
}
static void bpf_init(void)
{
memset(out, 0, sizeof(out));
labels = calloc(BPF_MAXINSNS, sizeof(*labels));
assert(labels);
labels_jt = calloc(BPF_MAXINSNS, sizeof(*labels_jt));
assert(labels_jt);
labels_jf = calloc(BPF_MAXINSNS, sizeof(*labels_jf));
assert(labels_jf);
labels_k = calloc(BPF_MAXINSNS, sizeof(*labels_k));
assert(labels_k);
}
static void bpf_destroy_labels(void)
{
int i;
for (i = 0; i < curr_instr; i++) {
free(labels_jf[i]);
free(labels_jt[i]);
free(labels_k[i]);
free(labels[i]);
}
}
static void bpf_destroy(void)
{
bpf_destroy_labels();
free(labels_jt);
free(labels_jf);
free(labels_k);
free(labels);
}
void bpf_asm_compile(FILE *fp, bool cstyle)
{
yyin = fp;
bpf_init();
bpf_stage_1_insert_insns();
bpf_stage_2_reduce_labels();
bpf_destroy();
if (cstyle)
bpf_pretty_print_c();
else
bpf_pretty_print();
if (fp != stdin)
fclose(yyin);
}
void yyerror(const char *str)
{
fprintf(stderr, "error: %s at line %d\n", str, yylineno);
exit(1);
}
+334
View File
@@ -0,0 +1,334 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Minimal BPF JIT image disassembler
*
* Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
* debugging or verification purposes.
*
* To get the disassembly of the JIT code, do the following:
*
* 1) `echo 2 > /proc/sys/net/core/bpf_jit_enable`
* 2) Load a BPF filter (e.g. `tcpdump -p -n -s 0 -i eth1 host 192.168.20.0/24`)
* 3) Run e.g. `bpf_jit_disasm -o` to read out the last JIT code
*
* Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <bfd.h>
#include <dis-asm.h>
#include <regex.h>
#include <fcntl.h>
#include <sys/klog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <tools/dis-asm-compat.h>
#define CMD_ACTION_SIZE_BUFFER 10
#define CMD_ACTION_READ_ALL 3
static void get_exec_path(char *tpath, size_t size)
{
char *path;
ssize_t len;
snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
tpath[size - 1] = 0;
path = strdup(tpath);
assert(path);
len = readlink(path, tpath, size);
if (len < 0)
len = 0;
tpath[len] = 0;
free(path);
}
static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
{
int count, i, pc = 0;
char tpath[PATH_MAX];
struct disassemble_info info;
disassembler_ftype disassemble;
bfd *bfdf;
memset(tpath, 0, sizeof(tpath));
get_exec_path(tpath, sizeof(tpath));
bfdf = bfd_openr(tpath, NULL);
assert(bfdf);
assert(bfd_check_format(bfdf, bfd_object));
init_disassemble_info_compat(&info, stdout,
(fprintf_ftype) fprintf,
fprintf_styled);
info.arch = bfd_get_arch(bfdf);
info.mach = bfd_get_mach(bfdf);
info.buffer = image;
info.buffer_length = len;
disassemble_init_for_target(&info);
#ifdef DISASM_FOUR_ARGS_SIGNATURE
disassemble = disassembler(info.arch,
bfd_big_endian(bfdf),
info.mach,
bfdf);
#else
disassemble = disassembler(bfdf);
#endif
assert(disassemble);
do {
printf("%4x:\t", pc);
count = disassemble(pc, &info);
if (opcodes) {
printf("\n\t");
for (i = 0; i < count; ++i)
printf("%02x ", (uint8_t) image[pc + i]);
}
printf("\n");
pc += count;
} while(count > 0 && pc < len);
bfd_close(bfdf);
}
static char *get_klog_buff(unsigned int *klen)
{
int ret, len;
char *buff;
len = klogctl(CMD_ACTION_SIZE_BUFFER, NULL, 0);
if (len < 0)
return NULL;
buff = malloc(len);
if (!buff)
return NULL;
ret = klogctl(CMD_ACTION_READ_ALL, buff, len);
if (ret < 0) {
free(buff);
return NULL;
}
*klen = ret;
return buff;
}
static char *get_flog_buff(const char *file, unsigned int *klen)
{
int fd, ret, len;
struct stat fi;
char *buff;
fd = open(file, O_RDONLY);
if (fd < 0)
return NULL;
ret = fstat(fd, &fi);
if (ret < 0 || !S_ISREG(fi.st_mode))
goto out;
len = fi.st_size + 1;
buff = malloc(len);
if (!buff)
goto out;
memset(buff, 0, len);
ret = read(fd, buff, len - 1);
if (ret <= 0)
goto out_free;
close(fd);
*klen = ret;
return buff;
out_free:
free(buff);
out:
close(fd);
return NULL;
}
static char *get_log_buff(const char *file, unsigned int *klen)
{
return file ? get_flog_buff(file, klen) : get_klog_buff(klen);
}
static void put_log_buff(char *buff)
{
free(buff);
}
static uint8_t *get_last_jit_image(char *haystack, size_t hlen,
unsigned int *ilen)
{
char *ptr, *pptr, *tmp;
off_t off = 0;
unsigned int proglen;
int ret, flen, pass, ulen = 0;
regmatch_t pmatch[1];
unsigned long base;
regex_t regex;
uint8_t *image;
if (hlen == 0)
return NULL;
ret = regcomp(&regex, "flen=[[:alnum:]]+ proglen=[[:digit:]]+ "
"pass=[[:digit:]]+ image=[[:xdigit:]]+", REG_EXTENDED);
assert(ret == 0);
ptr = haystack;
memset(pmatch, 0, sizeof(pmatch));
while (1) {
ret = regexec(&regex, ptr, 1, pmatch, 0);
if (ret == 0) {
ptr += pmatch[0].rm_eo;
off += pmatch[0].rm_eo;
assert(off < hlen);
} else
break;
}
ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
ret = sscanf(ptr, "flen=%d proglen=%u pass=%d image=%lx",
&flen, &proglen, &pass, &base);
if (ret != 4) {
regfree(&regex);
return NULL;
}
if (proglen > 1000000) {
printf("proglen of %u too big, stopping\n", proglen);
return NULL;
}
image = malloc(proglen);
if (!image) {
printf("Out of memory\n");
return NULL;
}
memset(image, 0, proglen);
tmp = ptr = haystack + off;
while ((ptr = strtok(tmp, "\n")) != NULL && ulen < proglen) {
tmp = NULL;
if (!strstr(ptr, "JIT code"))
continue;
pptr = ptr;
while ((ptr = strstr(pptr, ":")))
pptr = ptr + 1;
ptr = pptr;
do {
image[ulen++] = (uint8_t) strtoul(pptr, &pptr, 16);
if (ptr == pptr) {
ulen--;
break;
}
if (ulen >= proglen)
break;
ptr = pptr;
} while (1);
}
assert(ulen == proglen);
printf("%u bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
proglen, pass, flen);
printf("%lx + <x>:\n", base);
regfree(&regex);
*ilen = ulen;
return image;
}
static void usage(void)
{
printf("Usage: bpf_jit_disasm [...]\n");
printf(" -o Also display related opcodes (default: off).\n");
printf(" -O <file> Write binary image of code to file, don't disassemble to stdout.\n");
printf(" -f <file> Read last image dump from file or stdin (default: klog).\n");
printf(" -h Display this help.\n");
}
int main(int argc, char **argv)
{
unsigned int len, klen, opt, opcodes = 0;
char *kbuff, *file = NULL;
char *ofile = NULL;
int ofd;
ssize_t nr;
uint8_t *pos;
uint8_t *image = NULL;
while ((opt = getopt(argc, argv, "of:O:")) != -1) {
switch (opt) {
case 'o':
opcodes = 1;
break;
case 'O':
ofile = optarg;
break;
case 'f':
file = optarg;
break;
default:
usage();
return -1;
}
}
bfd_init();
kbuff = get_log_buff(file, &klen);
if (!kbuff) {
fprintf(stderr, "Could not retrieve log buffer!\n");
return -1;
}
image = get_last_jit_image(kbuff, klen, &len);
if (!image) {
fprintf(stderr, "No JIT image found!\n");
goto done;
}
if (!ofile) {
get_asm_insns(image, len, opcodes);
goto done;
}
ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
if (ofd < 0) {
fprintf(stderr, "Could not open file %s for writing: ", ofile);
perror(NULL);
goto done;
}
pos = image;
do {
nr = write(ofd, pos, len);
if (nr < 0) {
fprintf(stderr, "Could not write data to %s: ", ofile);
perror(NULL);
goto done;
}
len -= nr;
pos += nr;
} while (len);
close(ofd);
done:
put_log_buff(kbuff);
free(image);
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
*.d
/bootstrap/
/bpftool
bpftool*.8
FEATURE-DUMP.bpftool
feature
libbpf
/*.skel.h
/vmlinux.h
@@ -0,0 +1,54 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
include ../../../scripts/Makefile.include
INSTALL ?= install
RM ?= rm -f
RMDIR ?= rmdir --ignore-fail-on-non-empty
prefix ?= /usr/local
mandir ?= $(prefix)/man
man8dir = $(mandir)/man8
MAN8_RST = $(wildcard bpftool*.rst)
_DOC_MAN8 = $(patsubst %.rst,%.8,$(MAN8_RST))
DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8))
man: man8
man8: $(DOC_MAN8)
RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null)
RST2MAN_OPTS += --verbose --strip-comments
list_pages = $(sort $(basename $(filter-out $(1),$(MAN8_RST))))
see_also = $(subst " ",, \
"\n" \
"SEE ALSO\n" \
"========\n" \
"**bpf**\ (2),\n" \
"**bpf-helpers**\\ (7)" \
$(foreach page,$(call list_pages,$(1)),",\n**$(page)**\\ (8)") \
"\n")
$(OUTPUT)%.8: %.rst
ifndef RST2MAN_DEP
$(error "rst2man not found, but required to generate man pages")
endif
$(QUIET_GEN)( cat $< ; printf "%b" $(call see_also,$<) ) | rst2man $(RST2MAN_OPTS) > $@
clean:
$(call QUIET_CLEAN, Documentation)
$(Q)$(RM) $(DOC_MAN8)
install: man
$(call QUIET_INSTALL, Documentation-man)
$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir)
$(Q)$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir)
uninstall:
$(call QUIET_UNINST, Documentation-man)
$(Q)$(RM) $(addprefix $(DESTDIR)$(man8dir)/,$(_DOC_MAN8))
$(Q)$(RMDIR) $(DESTDIR)$(man8dir)
.PHONY: man man8 clean install uninstall
.DEFAULT_GOAL := man
@@ -0,0 +1,272 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-btf
================
-------------------------------------------------------------------------------
tool for inspection of BTF data
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **btf** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } }
*COMMANDS* := { **dump** | **help** }
BTF COMMANDS
=============
| **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
| **bpftool** **btf help**
|
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* [**file** *FILE*]... }
| *FORMAT* := { **raw** | **c** [**unsorted**] }
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
DESCRIPTION
===========
bpftool btf { show | list } [id *BTF_ID*]
Show information about loaded BTF objects. If a BTF ID is specified, show
information only about given BTF object, otherwise list all BTF objects
currently loaded on the system.
Since Linux 5.8 bpftool is able to discover information about processes
that hold open file descriptors (FDs) against BTF objects. On such kernels
bpftool will automatically emit this information as well.
bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
Dump BTF entries from a given *BTF_SRC*.
When **id** is specified, BTF object with that ID will be loaded and all
its BTF types emitted.
When **map** is provided, it's expected that map has associated BTF object
with BTF types describing key and value. It's possible to select whether to
dump only BTF type(s) associated with key (**key**), value (**value**),
both key and value (**kv**), or all BTF types present in associated BTF
object (**all**). If not specified, **kv** is assumed.
When **prog** is provided, it's expected that program has associated BTF
object with BTF types.
When specifying *FILE*, an ELF file or a raw BTF file (e.g. from
``/sys/kernel/btf/``) is expected. Multiple **file** arguments may be
given to merge BTF from several kernel modules into a single output.
When sysfs paths are used, vmlinux BTF is loaded automatically as the
base; if vmlinux itself appears in the file list it is skipped.
A base BTF can also be specified explicitly with **-B**.
**format** option can be used to override default (raw) output format. Raw
(**raw**) or C-syntax (**c**) output formats are supported. With C-style
formatting, the output is sorted by default. Use the **unsorted** option
to avoid sorting the output.
**root_id** option can be used to filter a dump to a single type and all
its dependent types. It cannot be used with any other types of filtering
(such as the "key", "value", or "kv" arguments when dumping BTF for a map).
It can be passed multiple times to dump multiple types.
bpftool btf help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
-B, --base-btf *FILE*
Pass a base BTF object. Base BTF objects are typically used with BTF
objects for kernel modules. To avoid duplicating all kernel symbols
required by modules, BTF objects for modules are "split", they are
built incrementally on top of the kernel (vmlinux) BTF object. So the
base BTF reference should usually point to the kernel BTF.
When the main BTF object to process (for example, the module BTF to
dump) is passed as a *FILE*, bpftool attempts to autodetect the path
for the base object, and passing this option is optional. When the main
BTF object is passed through other handles, this option becomes
necessary.
EXAMPLES
========
**# bpftool btf dump id 1226**
::
[1] PTR '(anon)' type_id=2
[2] STRUCT 'dummy_tracepoint_args' size=16 vlen=2
'pad' type_id=3 bits_offset=0
'sock' type_id=4 bits_offset=64
[3] INT 'long long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
[4] PTR '(anon)' type_id=5
[5] FWD 'sock' fwd_kind=union
This gives an example of default output for all supported BTF kinds.
**$ cat prog.c**
::
struct fwd_struct;
enum my_enum {
VAL1 = 3,
VAL2 = 7,
};
typedef struct my_struct my_struct_t;
struct my_struct {
const unsigned int const_int_field;
int bitfield_field: 4;
char arr_field[16];
const struct fwd_struct *restrict fwd_field;
enum my_enum enum_field;
volatile my_struct_t *typedef_ptr_field;
};
union my_union {
int a;
struct my_struct b;
};
struct my_struct struct_global_var __attribute__((section("data_sec"))) = {
.bitfield_field = 3,
.enum_field = VAL1,
};
int global_var __attribute__((section("data_sec"))) = 7;
__attribute__((noinline))
int my_func(union my_union *arg1, int arg2)
{
static int static_var __attribute__((section("data_sec"))) = 123;
static_var++;
return static_var;
}
**$ bpftool btf dump file prog.o**
::
[1] PTR '(anon)' type_id=2
[2] UNION 'my_union' size=48 vlen=2
'a' type_id=3 bits_offset=0
'b' type_id=4 bits_offset=0
[3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
[4] STRUCT 'my_struct' size=48 vlen=6
'const_int_field' type_id=5 bits_offset=0
'bitfield_field' type_id=3 bits_offset=32 bitfield_size=4
'arr_field' type_id=8 bits_offset=40
'fwd_field' type_id=10 bits_offset=192
'enum_field' type_id=14 bits_offset=256
'typedef_ptr_field' type_id=15 bits_offset=320
[5] CONST '(anon)' type_id=6
[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
[7] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
[8] ARRAY '(anon)' type_id=7 index_type_id=9 nr_elems=16
[9] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)
[10] RESTRICT '(anon)' type_id=11
[11] PTR '(anon)' type_id=12
[12] CONST '(anon)' type_id=13
[13] FWD 'fwd_struct' fwd_kind=union
[14] ENUM 'my_enum' size=4 vlen=2
'VAL1' val=3
'VAL2' val=7
[15] PTR '(anon)' type_id=16
[16] VOLATILE '(anon)' type_id=17
[17] TYPEDEF 'my_struct_t' type_id=4
[18] FUNC_PROTO '(anon)' ret_type_id=3 vlen=2
'arg1' type_id=1
'arg2' type_id=3
[19] FUNC 'my_func' type_id=18
[20] VAR 'struct_global_var' type_id=4, linkage=global-alloc
[21] VAR 'global_var' type_id=3, linkage=global-alloc
[22] VAR 'my_func.static_var' type_id=3, linkage=static
[23] DATASEC 'data_sec' size=0 vlen=3
type_id=20 offset=0 size=48
type_id=21 offset=0 size=4
type_id=22 offset=52 size=4
The following commands print BTF types associated with specified map's key,
value, both key and value, and all BTF types, respectively. By default, both
key and value types will be printed.
**# bpftool btf dump map id 123 key**
::
[39] TYPEDEF 'u32' type_id=37
**# bpftool btf dump map id 123 value**
::
[86] PTR '(anon)' type_id=87
**# bpftool btf dump map id 123 kv**
::
[39] TYPEDEF 'u32' type_id=37
[86] PTR '(anon)' type_id=87
**# bpftool btf dump map id 123 all**
::
[1] PTR '(anon)' type_id=0
.
.
.
[2866] ARRAY '(anon)' type_id=52 index_type_id=51 nr_elems=4
All the standard ways to specify map or program are supported:
**# bpftool btf dump map id 123**
**# bpftool btf dump map pinned /sys/fs/bpf/map_name**
**# bpftool btf dump prog id 456**
**# bpftool btf dump prog tag b88e0a09b1d9759d**
**# bpftool btf dump prog pinned /sys/fs/bpf/prog_name**
|
| **# bpftool btf dump file /sys/kernel/btf/i2c_smbus**
| (or)
| **# I2C_SMBUS_ID=$(bpftool btf show -p | jq '.[] | select(.name=="i2c_smbus").id')**
| **# bpftool btf dump id ${I2C_SMBUS_ID} -B /sys/kernel/btf/vmlinux**
::
[104848] STRUCT 'i2c_smbus_alert' size=40 vlen=2
'alert' type_id=393 bits_offset=0
'ara' type_id=56050 bits_offset=256
[104849] STRUCT 'alert_data' size=12 vlen=3
'addr' type_id=16 bits_offset=0
'type' type_id=56053 bits_offset=32
'data' type_id=7 bits_offset=64
[104850] PTR '(anon)' type_id=104848
[104851] PTR '(anon)' type_id=104849
[104852] FUNC 'i2c_register_spd' type_id=84745 linkage=static
[104853] FUNC 'smbalert_driver_init' type_id=1213 linkage=static
[104854] FUNC_PROTO '(anon)' ret_type_id=18 vlen=1
'ara' type_id=56050
[104855] FUNC 'i2c_handle_smbus_alert' type_id=104854 linkage=static
[104856] FUNC 'smbalert_remove' type_id=104854 linkage=static
[104857] FUNC_PROTO '(anon)' ret_type_id=18 vlen=2
'ara' type_id=56050
'id' type_id=56056
[104858] FUNC 'smbalert_probe' type_id=104857 linkage=static
[104859] FUNC 'smbalert_work' type_id=9695 linkage=static
[104860] FUNC 'smbus_alert' type_id=71367 linkage=static
[104861] FUNC 'smbus_do_alert' type_id=84827 linkage=static
@@ -0,0 +1,158 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-cgroup
================
-------------------------------------------------------------------------------
tool for inspection and simple manipulation of eBPF progs
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **cgroup** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| | { **-f** | **--bpffs** } }
*COMMANDS* :=
{ **show** | **list** | **tree** | **attach** | **detach** | **help** }
CGROUP COMMANDS
===============
| **bpftool** **cgroup** { **show** | **list** } *CGROUP* [**effective**]
| **bpftool** **cgroup tree** [*CGROUP_ROOT*] [**effective**]
| **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
| **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
| **bpftool** **cgroup help**
|
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
| *ATTACH_TYPE* := { **cgroup_inet_ingress** | **cgroup_inet_egress** |
| **cgroup_inet_sock_create** | **cgroup_sock_ops** |
| **cgroup_device** | **cgroup_inet4_bind** | **cgroup_inet6_bind** |
| **cgroup_inet4_post_bind** | **cgroup_inet6_post_bind** |
| **cgroup_inet4_connect** | **cgroup_inet6_connect** |
| **cgroup_unix_connect** | **cgroup_inet4_getpeername** |
| **cgroup_inet6_getpeername** | **cgroup_unix_getpeername** |
| **cgroup_inet4_getsockname** | **cgroup_inet6_getsockname** |
| **cgroup_unix_getsockname** | **cgroup_udp4_sendmsg** |
| **cgroup_udp6_sendmsg** | **cgroup_unix_sendmsg** |
| **cgroup_udp4_recvmsg** | **cgroup_udp6_recvmsg** |
| **cgroup_unix_recvmsg** | **cgroup_sysctl** |
| **cgroup_getsockopt** | **cgroup_setsockopt** |
| **cgroup_inet_sock_release** }
| *ATTACH_FLAGS* := { **multi** | **override** }
DESCRIPTION
===========
bpftool cgroup { show | list } *CGROUP* [effective]
List all programs attached to the cgroup *CGROUP*.
Output will start with program ID followed by attach type, attach flags and
program name.
If **effective** is specified retrieve effective programs that will execute
for events within a cgroup. This includes inherited along with attached
ones.
bpftool cgroup tree [*CGROUP_ROOT*] [effective]
Iterate over all cgroups in *CGROUP_ROOT* and list all attached programs.
If *CGROUP_ROOT* is not specified, bpftool uses cgroup v2 mountpoint.
The output is similar to the output of cgroup show/list commands: it starts
with absolute cgroup path, followed by program ID, attach type, attach
flags and program name.
If **effective** is specified retrieve effective programs that will execute
for events within a cgroup. This includes inherited along with attached
ones.
bpftool cgroup attach *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
Attach program *PROG* to the cgroup *CGROUP* with attach type *ATTACH_TYPE*
and optional *ATTACH_FLAGS*.
*ATTACH_FLAGS* can be one of: **override** if a sub-cgroup installs some
bpf program, the program in this cgroup yields to sub-cgroup program;
**multi** if a sub-cgroup installs some bpf program, that cgroup program
gets run in addition to the program in this cgroup.
Only one program is allowed to be attached to a cgroup with no attach flags
or the **override** flag. Attaching another program will release old
program and attach the new one.
Multiple programs are allowed to be attached to a cgroup with **multi**.
They are executed in FIFO order (those that were attached first, run
first).
Non-default *ATTACH_FLAGS* are supported by kernel version 4.14 and later.
*ATTACH_TYPE* can be one of:
- **ingress** ingress path of the inet socket (since 4.10)
- **egress** egress path of the inet socket (since 4.10)
- **sock_create** opening of an inet socket (since 4.10)
- **sock_ops** various socket operations (since 4.12)
- **device** device access (since 4.15)
- **bind4** call to bind(2) for an inet4 socket (since 4.17)
- **bind6** call to bind(2) for an inet6 socket (since 4.17)
- **post_bind4** return from bind(2) for an inet4 socket (since 4.17)
- **post_bind6** return from bind(2) for an inet6 socket (since 4.17)
- **connect4** call to connect(2) for an inet4 socket (since 4.17)
- **connect6** call to connect(2) for an inet6 socket (since 4.17)
- **connect_unix** call to connect(2) for a unix socket (since 6.7)
- **sendmsg4** call to sendto(2), sendmsg(2), sendmmsg(2) for an unconnected udp4 socket (since 4.18)
- **sendmsg6** call to sendto(2), sendmsg(2), sendmmsg(2) for an unconnected udp6 socket (since 4.18)
- **sendmsg_unix** call to sendto(2), sendmsg(2), sendmmsg(2) for an unconnected unix socket (since 6.7)
- **recvmsg4** call to recvfrom(2), recvmsg(2), recvmmsg(2) for an unconnected udp4 socket (since 5.2)
- **recvmsg6** call to recvfrom(2), recvmsg(2), recvmmsg(2) for an unconnected udp6 socket (since 5.2)
- **recvmsg_unix** call to recvfrom(2), recvmsg(2), recvmmsg(2) for an unconnected unix socket (since 6.7)
- **sysctl** sysctl access (since 5.2)
- **getsockopt** call to getsockopt (since 5.3)
- **setsockopt** call to setsockopt (since 5.3)
- **getpeername4** call to getpeername(2) for an inet4 socket (since 5.8)
- **getpeername6** call to getpeername(2) for an inet6 socket (since 5.8)
- **getpeername_unix** call to getpeername(2) for a unix socket (since 6.7)
- **getsockname4** call to getsockname(2) for an inet4 socket (since 5.8)
- **getsockname6** call to getsockname(2) for an inet6 socket (since 5.8)
- **getsockname_unix** call to getsockname(2) for a unix socket (since 6.7)
- **sock_release** closing a userspace inet socket (since 5.9)
bpftool cgroup detach *CGROUP* *ATTACH_TYPE* *PROG*
Detach *PROG* from the cgroup *CGROUP* and attach type *ATTACH_TYPE*.
bpftool prog help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
-f, --bpffs
Show file names of pinned programs.
EXAMPLES
========
|
| **# mount -t bpf none /sys/fs/bpf/**
| **# mkdir /sys/fs/cgroup/test.slice**
| **# bpftool prog load ./device_cgroup.o /sys/fs/bpf/prog**
| **# bpftool cgroup attach /sys/fs/cgroup/test.slice/ device id 1 allow_multi**
**# bpftool cgroup list /sys/fs/cgroup/test.slice/**
::
ID AttachType AttachFlags Name
1 device allow_multi bpf_prog1
|
| **# bpftool cgroup detach /sys/fs/cgroup/test.slice/ device id 1**
| **# bpftool cgroup list /sys/fs/cgroup/test.slice/**
::
ID AttachType AttachFlags Name
@@ -0,0 +1,83 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
===============
bpftool-feature
===============
-------------------------------------------------------------------------------
tool for inspection of eBPF-related parameters for Linux kernel or net device
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **feature** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| }
*COMMANDS* := { **probe** | **help** }
FEATURE COMMANDS
================
| **bpftool** **feature probe** [*COMPONENT*] [**full**] [**unprivileged**] [**macros** [**prefix** *PREFIX*]]
| **bpftool** **feature list_builtins** *GROUP*
| **bpftool** **feature help**
|
| *COMPONENT* := { **kernel** | **dev** *NAME* }
| *GROUP* := { **prog_types** | **map_types** | **attach_types** | **link_types** | **helpers** }
DESCRIPTION
===========
bpftool feature probe [kernel] [full] [macros [prefix *PREFIX*]]
Probe the running kernel and dump a number of eBPF-related parameters, such
as availability of the **bpf**\ () system call, JIT status, eBPF program
types availability, eBPF helper functions availability, and more.
By default, bpftool **does not run probes** for **bpf_probe_write_user**\
() and **bpf_trace_printk**\() helpers which print warnings to kernel logs.
To enable them and run all probes, the **full** keyword should be used.
If the **macros** keyword (but not the **-j** option) is passed, a subset
of the output is dumped as a list of **#define** macros that are ready to
be included in a C header file, for example. If, additionally, **prefix**
is used to define a *PREFIX*, the provided string will be used as a prefix
to the names of the macros: this can be used to avoid conflicts on macro
names when including the output of this command as a header file.
Keyword **kernel** can be omitted. If no probe target is specified, probing
the kernel is the default behaviour.
When the **unprivileged** keyword is used, bpftool will dump only the
features available to a user who does not have the **CAP_SYS_ADMIN**
capability set. The features available in that case usually represent a
small subset of the parameters supported by the system. Unprivileged users
MUST use the **unprivileged** keyword: This is to avoid misdetection if
bpftool is inadvertently run as non-root, for example. This keyword is
unavailable if bpftool was compiled without libcap.
bpftool feature probe dev *NAME* [full] [macros [prefix *PREFIX*]]
Probe network device for supported eBPF features and dump results to the
console.
The keywords **full**, **macros** and **prefix** have the same role as when
probing the kernel.
bpftool feature list_builtins *GROUP*
List items known to bpftool. These can be BPF program types
(**prog_types**), BPF map types (**map_types**), attach types
(**attach_types**), link types (**link_types**), or BPF helper functions
(**helpers**). The command does not probe the system, but simply lists the
elements that bpftool knows from compilation time, as provided from libbpf
(for all object types) or from the BPF UAPI header (list of helpers). This
can be used in scripts to iterate over BPF types or helpers.
bpftool feature help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
@@ -0,0 +1,481 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-gen
================
-------------------------------------------------------------------------------
tool for BPF code-generation
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **gen** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
*COMMAND* := { **object** | **skeleton** | **help** }
GEN COMMANDS
=============
| **bpftool** **gen object** *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
| **bpftool** **gen skeleton** *FILE* [**name** *OBJECT_NAME*]
| **bpftool** **gen subskeleton** *FILE* [**name** *OBJECT_NAME*]
| **bpftool** **gen min_core_btf** *INPUT* *OUTPUT* *OBJECT* [*OBJECT*...]
| **bpftool** **gen help**
DESCRIPTION
===========
bpftool gen object *OUTPUT_FILE* *INPUT_FILE* [*INPUT_FILE*...]
Statically link (combine) together one or more *INPUT_FILE*'s into a single
resulting *OUTPUT_FILE*. All the files involved are BPF ELF object files.
The rules of BPF static linking are mostly the same as for user-space
object files, but in addition to combining data and instruction sections,
.BTF and .BTF.ext (if present in any of the input files) data are combined
together. .BTF data is deduplicated, so all the common types across
*INPUT_FILE*'s will only be represented once in the resulting BTF
information.
BPF static linking allows to partition BPF source code into individually
compiled files that are then linked into a single resulting BPF object
file, which can be used to generated BPF skeleton (with **gen skeleton**
command) or passed directly into **libbpf** (using **bpf_object__open()**
family of APIs).
bpftool gen skeleton *FILE*
Generate BPF skeleton C header file for a given *FILE*.
BPF skeleton is an alternative interface to existing libbpf APIs for
working with BPF objects. Skeleton code is intended to significantly
shorten and simplify code to load and work with BPF programs from userspace
side. Generated code is tailored to specific input BPF object *FILE*,
reflecting its structure by listing out available maps, program, variables,
etc. Skeleton eliminates the need to lookup mentioned components by name.
Instead, if skeleton instantiation succeeds, they are populated in skeleton
structure as valid libbpf types (e.g., **struct bpf_map** pointer) and can
be passed to existing generic libbpf APIs.
In addition to simple and reliable access to maps and programs, skeleton
provides a storage for BPF links (**struct bpf_link**) for each BPF program
within BPF object. When requested, supported BPF programs will be
automatically attached and resulting BPF links stored for further use by
user in pre-allocated fields in skeleton struct. For BPF programs that
can't be automatically attached by libbpf, user can attach them manually,
but store resulting BPF link in per-program link field. All such set up
links will be automatically destroyed on BPF skeleton destruction. This
eliminates the need for users to manage links manually and rely on libbpf
support to detach programs and free up resources.
Another facility provided by BPF skeleton is an interface to global
variables of all supported kinds: mutable, read-only, as well as extern
ones. This interface allows to pre-setup initial values of variables before
BPF object is loaded and verified by kernel. For non-read-only variables,
the same interface can be used to fetch values of global variables on
userspace side, even if they are modified by BPF code.
During skeleton generation, contents of source BPF object *FILE* is
embedded within generated code and is thus not necessary to keep around.
This ensures skeleton and BPF object file are matching 1-to-1 and always
stay in sync. Generated code is dual-licensed under LGPL-2.1 and
BSD-2-Clause licenses.
It is a design goal and guarantee that skeleton interfaces are
interoperable with generic libbpf APIs. User should always be able to use
skeleton API to create and load BPF object, and later use libbpf APIs to
keep working with specific maps, programs, etc.
As part of skeleton, few custom functions are generated. Each of them is
prefixed with object name. Object name can either be derived from object
file name, i.e., if BPF object file name is **example.o**, BPF object name
will be **example**. Object name can be also specified explicitly through
**name** *OBJECT_NAME* parameter. The following custom functions are
provided (assuming **example** as the object name):
- **example__open** and **example__open_opts**.
These functions are used to instantiate skeleton. It corresponds to
libbpf's **bpf_object__open**\ () API. **_opts** variants accepts extra
**bpf_object_open_opts** options.
- **example__load**.
This function creates maps, loads and verifies BPF programs, initializes
global data maps. It corresponds to libbpf's **bpf_object__load**\ ()
API.
- **example__open_and_load** combines **example__open** and
**example__load** invocations in one commonly used operation.
- **example__attach** and **example__detach**.
This pair of functions allow to attach and detach, correspondingly,
already loaded BPF object. Only BPF programs of types supported by libbpf
for auto-attachment will be auto-attached and their corresponding BPF
links instantiated. For other BPF programs, user can manually create a
BPF link and assign it to corresponding fields in skeleton struct.
**example__detach** will detach both links created automatically, as well
as those populated by user manually.
- **example__destroy**.
Detach and unload BPF programs, free up all the resources used by
skeleton and BPF object.
If BPF object has global variables, corresponding structs with memory
layout corresponding to global data data section layout will be created.
Currently supported ones are: *.data*, *.bss*, *.rodata*, and *.kconfig*
structs/data sections. These data sections/structs can be used to set up
initial values of variables, if set before **example__load**. Afterwards,
if target kernel supports memory-mapped BPF arrays, same structs can be
used to fetch and update (non-read-only) data from userspace, with same
simplicity as for BPF side.
bpftool gen subskeleton *FILE*
Generate BPF subskeleton C header file for a given *FILE*.
Subskeletons are similar to skeletons, except they do not own the
corresponding maps, programs, or global variables. They require that the
object file used to generate them is already loaded into a *bpf_object* by
some other means.
This functionality is useful when a library is included into a larger BPF
program. A subskeleton for the library would have access to all objects and
globals defined in it, without having to know about the larger program.
Consequently, there are only two functions defined for subskeletons:
- **example__open(bpf_object\*)**.
Instantiates a subskeleton from an already opened (but not necessarily
loaded) **bpf_object**.
- **example__destroy()**.
Frees the storage for the subskeleton but *does not* unload any BPF
programs or maps.
bpftool gen min_core_btf *INPUT* *OUTPUT* *OBJECT* [*OBJECT*...]
Generate a minimum BTF file as *OUTPUT*, derived from a given *INPUT* BTF
file, containing all needed BTF types so one, or more, given eBPF objects
CO-RE relocations may be satisfied.
When kernels aren't compiled with CONFIG_DEBUG_INFO_BTF, libbpf, when
loading an eBPF object, has to rely on external BTF files to be able to
calculate CO-RE relocations.
Usually, an external BTF file is built from existing kernel DWARF data
using pahole. It contains all the types used by its respective kernel image
and, because of that, is big.
The min_core_btf feature builds smaller BTF files, customized to one or
multiple eBPF objects, so they can be distributed together with an eBPF
CO-RE based application, turning the application portable to different
kernel versions.
Check examples below for more information on how to use it.
bpftool gen help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
-L, --use-loader
For skeletons, generate a "light" skeleton (also known as "loader"
skeleton). A light skeleton contains a loader eBPF program. It does not use
the majority of the libbpf infrastructure, and does not need libelf.
-S, --sign
For skeletons, generate a signed skeleton. This option must be used with
**-k** and **-i**. Using this flag implicitly enables **--use-loader**.
-k <private_key.pem>
Path to the private key file in PEM format, required for signing.
-i <certificate.x509>
Path to the X.509 certificate file in PEM or DER format, required for
signing.
EXAMPLES
========
**$ cat example1.bpf.c**
::
#include <stdbool.h>
#include <linux/ptrace.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
const volatile int param1 = 42;
bool global_flag = true;
struct { int x; } data = {};
SEC("raw_tp/sys_enter")
int handle_sys_enter(struct pt_regs *ctx)
{
static long my_static_var;
if (global_flag)
my_static_var++;
else
data.x += param1;
return 0;
}
**$ cat example2.bpf.c**
::
#include <linux/ptrace.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 128);
__type(key, int);
__type(value, long);
} my_map SEC(".maps");
SEC("raw_tp/sys_exit")
int handle_sys_exit(struct pt_regs *ctx)
{
int zero = 0;
bpf_map_lookup_elem(&my_map, &zero);
return 0;
}
**$ cat example3.bpf.c**
::
#include <linux/ptrace.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
/* This header file is provided by the bpf_testmod module. */
#include "bpf_testmod.h"
int test_2_result = 0;
/* bpf_Testmod.ko calls this function, passing a "4"
* and testmod_map->data.
*/
SEC("struct_ops/test_2")
void BPF_PROG(test_2, int a, int b)
{
test_2_result = a + b;
}
SEC(".struct_ops")
struct bpf_testmod_ops testmod_map = {
.test_2 = (void *)test_2,
.data = 0x1,
};
This is example BPF application with three BPF programs and a mix of BPF
maps and global variables. Source code is split across three source code
files.
**$ clang --target=bpf -g example1.bpf.c -o example1.bpf.o**
**$ clang --target=bpf -g example2.bpf.c -o example2.bpf.o**
**$ clang --target=bpf -g example3.bpf.c -o example3.bpf.o**
**$ bpftool gen object example.bpf.o example1.bpf.o example2.bpf.o example3.bpf.o**
This set of commands compiles *example1.bpf.c*, *example2.bpf.c* and
*example3.bpf.c* individually and then statically links respective object
files into the final BPF ELF object file *example.bpf.o*.
**$ bpftool gen skeleton example.bpf.o name example | tee example.skel.h**
::
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* THIS FILE IS AUTOGENERATED! */
#ifndef __EXAMPLE_SKEL_H__
#define __EXAMPLE_SKEL_H__
#include <stdlib.h>
#include <bpf/libbpf.h>
struct example {
struct bpf_object_skeleton *skeleton;
struct bpf_object *obj;
struct {
struct bpf_map *rodata;
struct bpf_map *data;
struct bpf_map *bss;
struct bpf_map *my_map;
struct bpf_map *testmod_map;
} maps;
struct {
struct example__testmod_map__bpf_testmod_ops {
const struct bpf_program *test_1;
const struct bpf_program *test_2;
int data;
} *testmod_map;
} struct_ops;
struct {
struct bpf_program *handle_sys_enter;
struct bpf_program *handle_sys_exit;
} progs;
struct {
struct bpf_link *handle_sys_enter;
struct bpf_link *handle_sys_exit;
} links;
struct example__bss {
struct {
int x;
} data;
int test_2_result;
} *bss;
struct example__data {
_Bool global_flag;
long int handle_sys_enter_my_static_var;
} *data;
struct example__rodata {
int param1;
} *rodata;
};
static void example__destroy(struct example *obj);
static inline struct example *example__open_opts(
const struct bpf_object_open_opts *opts);
static inline struct example *example__open();
static inline int example__load(struct example *obj);
static inline struct example *example__open_and_load();
static inline int example__attach(struct example *obj);
static inline void example__detach(struct example *obj);
#endif /* __EXAMPLE_SKEL_H__ */
**$ cat example.c**
::
#include "example.skel.h"
int main()
{
struct example *skel;
int err = 0;
skel = example__open();
if (!skel)
goto cleanup;
skel->rodata->param1 = 128;
/* Change the value through the pointer of shadow type */
skel->struct_ops.testmod_map->data = 13;
err = example__load(skel);
if (err)
goto cleanup;
/* The result of the function test_2() */
printf("test_2_result: %d\n", skel->bss->test_2_result);
err = example__attach(skel);
if (err)
goto cleanup;
/* all libbpf APIs are usable */
printf("my_map name: %s\n", bpf_map__name(skel->maps.my_map));
printf("sys_enter prog FD: %d\n",
bpf_program__fd(skel->progs.handle_sys_enter));
/* detach and re-attach sys_exit program */
bpf_link__destroy(skel->links.handle_sys_exit);
skel->links.handle_sys_exit =
bpf_program__attach(skel->progs.handle_sys_exit);
printf("my_static_var: %ld\n",
skel->bss->handle_sys_enter_my_static_var);
cleanup:
example__destroy(skel);
return err;
}
**# ./example**
::
test_2_result: 17
my_map name: my_map
sys_enter prog FD: 8
my_static_var: 7
This is a stripped-out version of skeleton generated for above example code.
min_core_btf
------------
**$ bpftool btf dump file 5.4.0-example.btf format raw**
::
[1] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
[2] CONST '(anon)' type_id=1
[3] VOLATILE '(anon)' type_id=1
[4] ARRAY '(anon)' type_id=1 index_type_id=21 nr_elems=2
[5] PTR '(anon)' type_id=8
[6] CONST '(anon)' type_id=5
[7] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)
[8] CONST '(anon)' type_id=7
[9] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
<long output>
**$ bpftool btf dump file one.bpf.o format raw**
::
[1] PTR '(anon)' type_id=2
[2] STRUCT 'trace_event_raw_sys_enter' size=64 vlen=4
'ent' type_id=3 bits_offset=0
'id' type_id=7 bits_offset=64
'args' type_id=9 bits_offset=128
'__data' type_id=12 bits_offset=512
[3] STRUCT 'trace_entry' size=8 vlen=4
'type' type_id=4 bits_offset=0
'flags' type_id=5 bits_offset=16
'preempt_count' type_id=5 bits_offset=24
<long output>
**$ bpftool gen min_core_btf 5.4.0-example.btf 5.4.0-smaller.btf one.bpf.o**
**$ bpftool btf dump file 5.4.0-smaller.btf format raw**
::
[1] TYPEDEF 'pid_t' type_id=6
[2] STRUCT 'trace_event_raw_sys_enter' size=64 vlen=1
'args' type_id=4 bits_offset=128
[3] STRUCT 'task_struct' size=9216 vlen=2
'pid' type_id=1 bits_offset=17920
'real_parent' type_id=7 bits_offset=18048
[4] ARRAY '(anon)' type_id=5 index_type_id=8 nr_elems=6
[5] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none)
[6] TYPEDEF '__kernel_pid_t' type_id=8
[7] PTR '(anon)' type_id=3
[8] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
<end>
Now, the "5.4.0-smaller.btf" file may be used by libbpf as an external BTF file
when loading the "one.bpf.o" object into the "5.4.0-example" kernel. Note that
the generated BTF file won't allow other eBPF objects to be loaded, just the
ones given to min_core_btf.
::
LIBBPF_OPTS(bpf_object_open_opts, opts, .btf_custom_path = "5.4.0-smaller.btf");
struct bpf_object *obj;
obj = bpf_object__open_file("one.bpf.o", &opts);
...
@@ -0,0 +1,72 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
============
bpftool-iter
============
-------------------------------------------------------------------------------
tool to create BPF iterators
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **iter** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| }
*COMMANDS* := { **pin** | **help** }
ITER COMMANDS
=============
| **bpftool** **iter pin** *OBJ* *PATH* [**map** *MAP*]
| **bpftool** **iter help**
|
| *OBJ* := /a/file/of/bpf_iter_target.o
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
DESCRIPTION
===========
bpftool iter pin *OBJ* *PATH* [map *MAP*]
A bpf iterator combines a kernel iterating of particular kernel data (e.g.,
tasks, bpf_maps, etc.) and a bpf program called for each kernel data object
(e.g., one task, one bpf_map, etc.). User space can *read* kernel iterator
output through *read()* syscall.
The *pin* command creates a bpf iterator from *OBJ*, and pin it to *PATH*.
The *PATH* should be located in *bpffs* mount. It must not contain a dot
character ('.'), which is reserved for future extensions of *bpffs*.
Map element bpf iterator requires an additional parameter *MAP* so bpf
program can iterate over map elements for that map. User can have a bpf
program in kernel to run with each map element, do checking, filtering,
aggregation, etc. without copying data to user space.
User can then *cat PATH* to see the bpf iterator output.
bpftool iter help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
EXAMPLES
========
**# bpftool iter pin bpf_iter_netlink.o /sys/fs/bpf/my_netlink**
::
Create a file-based bpf iterator from bpf_iter_netlink.o and pin it
to /sys/fs/bpf/my_netlink
**# bpftool iter pin bpf_iter_hashmap.o /sys/fs/bpf/my_hashmap map id 20**
::
Create a file-based bpf iterator from bpf_iter_hashmap.o and map with
id 20, and pin it to /sys/fs/bpf/my_hashmap
@@ -0,0 +1,107 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-link
================
-------------------------------------------------------------------------------
tool for inspection and simple manipulation of eBPF links
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **link** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| | { **-f** | **--bpffs** } | { **-n** | **--nomount** } }
*COMMANDS* := { **show** | **list** | **pin** | **help** }
LINK COMMANDS
=============
| **bpftool** **link { show | list }** [*LINK*]
| **bpftool** **link pin** *LINK* *FILE*
| **bpftool** **link detach** *LINK*
| **bpftool** **link help**
|
| *LINK* := { **id** *LINK_ID* | **pinned** *FILE* }
DESCRIPTION
===========
bpftool link { show | list } [*LINK*]
Show information about active links. If *LINK* is specified show
information only about given link, otherwise list all links currently
active on the system.
Output will start with link ID followed by link type and zero or more named
attributes, some of which depend on type of link.
Since Linux 5.8 bpftool is able to discover information about processes
that hold open file descriptors (FDs) against BPF links. On such kernels
bpftool will automatically emit this information as well.
bpftool link pin *LINK* *FILE*
Pin link *LINK* as *FILE*.
Note: *FILE* must be located in *bpffs* mount. It must not contain a dot
character ('.'), which is reserved for future extensions of *bpffs*.
bpftool link detach *LINK*
Force-detach link *LINK*. BPF link and its underlying BPF program will stay
valid, but they will be detached from the respective BPF hook and BPF link
will transition into a defunct state until last open file descriptor for
that link is closed.
bpftool link help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
-f, --bpffs
When showing BPF links, show file names of pinned links.
-n, --nomount
Do not automatically attempt to mount any virtual file system (such as
tracefs or BPF virtual file system) when necessary.
EXAMPLES
========
**# bpftool link show**
::
10: cgroup prog 25
cgroup_id 614 attach_type egress
pids test_progs(223)
**# bpftool --json --pretty link show**
::
[{
"type": "cgroup",
"prog_id": 25,
"cgroup_id": 614,
"attach_type": "egress",
"pids": [{
"pid": 223,
"comm": "test_progs"
}
]
}
]
|
| **# bpftool link pin id 10 /sys/fs/bpf/link**
| **# ls -l /sys/fs/bpf/**
::
-rw------- 1 root root 0 Apr 23 21:39 link
@@ -0,0 +1,272 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-map
================
-------------------------------------------------------------------------------
tool for inspection and simple manipulation of eBPF maps
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **map** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| | { **-f** | **--bpffs** } | { **-n** | **--nomount** } }
*COMMANDS* :=
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** |
**delete** | **pin** | **help** }
MAP COMMANDS
=============
| **bpftool** **map** { **show** | **list** } [*MAP*]
| **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \
| **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] \
| [**offload_dev** *NAME*]
| **bpftool** **map dump** *MAP*
| **bpftool** **map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*]
| **bpftool** **map lookup** *MAP* [**key** *DATA*]
| **bpftool** **map getnext** *MAP* [**key** *DATA*]
| **bpftool** **map delete** *MAP* **key** *DATA*
| **bpftool** **map pin** *MAP* *FILE*
| **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*]
| **bpftool** **map peek** *MAP*
| **bpftool** **map push** *MAP* **value** *VALUE*
| **bpftool** **map pop** *MAP*
| **bpftool** **map enqueue** *MAP* **value** *VALUE*
| **bpftool** **map dequeue** *MAP*
| **bpftool** **map freeze** *MAP*
| **bpftool** **map help**
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* | **name** *MAP_NAME* }
| *DATA* := { [**hex**] *BYTES* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
| *VALUE* := { *DATA* | *MAP* | *PROG* }
| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
| *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash**
| | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash**
| | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
| | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
| | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage**
| | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage**
| | **task_storage** | **bloom_filter** | **user_ringbuf** | **cgrp_storage** | **arena**
| | **insn_array** | **rhash** }
DESCRIPTION
===========
bpftool map { show | list } [*MAP*]
Show information about loaded maps. If *MAP* is specified show information
only about given maps, otherwise list all maps currently loaded on the
system. In case of **name**, *MAP* may match several maps which will all
be shown.
Output will start with map ID followed by map type and zero or more named
attributes (depending on kernel version).
Since Linux 5.8 bpftool is able to discover information about processes
that hold open file descriptors (FDs) against BPF maps. On such kernels
bpftool will automatically emit this information as well.
bpftool map create *FILE* type *TYPE* key *KEY_SIZE* value *VALUE_SIZE* entries *MAX_ENTRIES* name *NAME* [flags *FLAGS*] [inner_map *MAP*] [offload_dev *NAME*]
Create a new map with given parameters and pin it to *bpffs* as *FILE*.
*FLAGS* should be an integer which is the combination of desired flags,
e.g. 1024 for **BPF_F_MMAPABLE** (see bpf.h UAPI header for existing
flags).
To create maps of type array-of-maps or hash-of-maps, the **inner_map**
keyword must be used to pass an inner map. The kernel needs it to collect
metadata related to the inner maps that the new map will work with.
Keyword **offload_dev** expects a network interface name, and is used to
request hardware offload for the map.
bpftool map dump *MAP*
Dump all entries in a given *MAP*. In case of **name**, *MAP* may match
several maps which will all be dumped.
bpftool map update *MAP* [key *DATA*] [value *VALUE*] [*UPDATE_FLAGS*]
Update map entry for a given *KEY*.
*UPDATE_FLAGS* can be one of: **any** update existing entry or add if
doesn't exit; **exist** update only if entry already exists; **noexist**
update only if entry doesn't exist.
If the **hex** keyword is provided in front of the bytes sequence, the
bytes are parsed as hexadecimal values, even if no "0x" prefix is added. If
the keyword is not provided, then the bytes are parsed as decimal values,
unless a "0x" prefix (for hexadecimal) or a "0" prefix (for octal) is
provided.
bpftool map lookup *MAP* [key *DATA*]
Lookup **key** in the map.
bpftool map getnext *MAP* [key *DATA*]
Get next key. If *key* is not specified, get first key.
bpftool map delete *MAP* key *DATA*
Remove entry from the map.
bpftool map pin *MAP* *FILE*
Pin map *MAP* as *FILE*.
Note: *FILE* must be located in *bpffs* mount. It must not contain a dot
character ('.'), which is reserved for future extensions of *bpffs*.
bpftool map event_pipe *MAP* [cpu *N* index *M*]
Read events from a **BPF_MAP_TYPE_PERF_EVENT_ARRAY** map.
Install perf rings into a perf event array map and dump output of any
**bpf_perf_event_output**\ () call in the kernel. By default read the
number of CPUs on the system and install perf ring for each CPU in the
corresponding index in the array.
If **cpu** and **index** are specified, install perf ring for given **cpu**
at **index** in the array (single ring).
Note that installing a perf ring into an array will silently replace any
existing ring. Any other application will stop receiving events if it
installed its rings earlier.
bpftool map peek *MAP*
Peek next value in the queue or stack.
bpftool map push *MAP* value *VALUE*
Push *VALUE* onto the stack.
bpftool map pop *MAP*
Pop and print value from the stack.
bpftool map enqueue *MAP* value *VALUE*
Enqueue *VALUE* into the queue.
bpftool map dequeue *MAP*
Dequeue and print value from the queue.
bpftool map freeze *MAP*
Freeze the map as read-only from user space. Entries from a frozen map can
not longer be updated or deleted with the **bpf**\ () system call. This
operation is not reversible, and the map remains immutable from user space
until its destruction. However, read and write permissions for BPF programs
to the map remain unchanged.
bpftool map help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
-f, --bpffs
Show file names of pinned maps.
-n, --nomount
Do not automatically attempt to mount any virtual file system (such as
tracefs or BPF virtual file system) when necessary.
EXAMPLES
========
**# bpftool map show**
::
10: hash name some_map flags 0x0
key 4B value 8B max_entries 2048 memlock 167936B
pids systemd(1)
The following three commands are equivalent:
|
| **# bpftool map update id 10 key hex 20 c4 b7 00 value hex 0f ff ff ab 01 02 03 4c**
| **# bpftool map update id 10 key 0x20 0xc4 0xb7 0x00 value 0x0f 0xff 0xff 0xab 0x01 0x02 0x03 0x4c**
| **# bpftool map update id 10 key 32 196 183 0 value 15 255 255 171 1 2 3 76**
**# bpftool map lookup id 10 key 0 1 2 3**
::
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
**# bpftool map dump id 10**
::
key: 00 01 02 03 value: 00 01 02 03 04 05 06 07
key: 0d 00 07 00 value: 02 00 00 00 01 02 03 04
Found 2 elements
**# bpftool map getnext id 10 key 0 1 2 3**
::
key:
00 01 02 03
next key:
0d 00 07 00
|
| **# mount -t bpf none /sys/fs/bpf/**
| **# bpftool map pin id 10 /sys/fs/bpf/map**
| **# bpftool map del pinned /sys/fs/bpf/map key 13 00 07 00**
Note that map update can also be used in order to change the program references
hold by a program array map. This can be used, for example, to change the
programs used for tail-call jumps at runtime, without having to reload the
entry-point program. Below is an example for this use case: we load a program
defining a prog array map, and with a main function that contains a tail call
to other programs that can be used either to "process" packets or to "debug"
processing. Note that the prog array map MUST be pinned into the BPF virtual
file system for the map update to work successfully, as kernel flushes prog
array maps when they have no more references from user space (and the update
would be lost as soon as bpftool exits).
|
| **# bpftool prog loadall tail_calls.o /sys/fs/bpf/foo type xdp**
| **# bpftool prog --bpffs**
::
545: xdp name main_func tag 674b4b5597193dc3 gpl
loaded_at 2018-12-12T15:02:58+0000 uid 0
xlated 240B jited 257B memlock 4096B map_ids 294
pinned /sys/fs/bpf/foo/xdp
546: xdp name bpf_func_process tag e369a529024751fc gpl
loaded_at 2018-12-12T15:02:58+0000 uid 0
xlated 200B jited 164B memlock 4096B
pinned /sys/fs/bpf/foo/process
547: xdp name bpf_func_debug tag 0b597868bc7f0976 gpl
loaded_at 2018-12-12T15:02:58+0000 uid 0
xlated 200B jited 164B memlock 4096B
pinned /sys/fs/bpf/foo/debug
**# bpftool map**
::
294: prog_array name jmp_table flags 0x0
key 4B value 4B max_entries 1 memlock 4096B
owner_prog_type xdp owner jited
|
| **# bpftool map pin id 294 /sys/fs/bpf/bar**
| **# bpftool map dump pinned /sys/fs/bpf/bar**
::
Found 0 elements
|
| **# bpftool map update pinned /sys/fs/bpf/bar key 0 0 0 0 value pinned /sys/fs/bpf/foo/debug**
| **# bpftool map dump pinned /sys/fs/bpf/bar**
::
key: 00 00 00 00 value: 22 02 00 00
Found 1 element
@@ -0,0 +1,218 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-net
================
-------------------------------------------------------------------------------
tool for inspection of networking related bpf prog attachments
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **net** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| }
*COMMANDS* := { **show** | **list** | **attach** | **detach** | **help** }
NET COMMANDS
============
| **bpftool** **net** { **show** | **list** } [ **dev** *NAME* ]
| **bpftool** **net attach** *ATTACH_TYPE* *PROG* **dev** *NAME* [ **overwrite** | **prepend** ]
| **bpftool** **net detach** *ATTACH_TYPE* **dev** *NAME*
| **bpftool** **net help**
|
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
| *ATTACH_TYPE* := { **xdp** | **xdpgeneric** | **xdpdrv** | **xdpoffload** | **tcx_ingress** | **tcx_egress** }
DESCRIPTION
===========
bpftool net { show | list } [ dev *NAME* ]
List bpf program attachments in the kernel networking subsystem.
Currently, device driver xdp attachments, tcx, netkit and old-style tc
classifier/action attachments, flow_dissector as well as netfilter
attachments are implemented, i.e., for program types **BPF_PROG_TYPE_XDP**,
**BPF_PROG_TYPE_SCHED_CLS**, **BPF_PROG_TYPE_SCHED_ACT**,
**BPF_PROG_TYPE_FLOW_DISSECTOR**, **BPF_PROG_TYPE_NETFILTER**.
For programs attached to a particular cgroup, e.g.,
**BPF_PROG_TYPE_CGROUP_SKB**, **BPF_PROG_TYPE_CGROUP_SOCK**,
**BPF_PROG_TYPE_SOCK_OPS** and **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**, users
can use **bpftool cgroup** to dump cgroup attachments. For sk_{filter, skb,
msg, reuseport} and lwt/seg6 bpf programs, users should consult other
tools, e.g., iproute2.
The current output will start with all xdp program attachments, followed by
all tcx, netkit, then tc class/qdisc bpf program attachments, then
flow_dissector and finally netfilter programs. Both xdp programs and
tcx/netkit/tc programs are ordered based on ifindex number. If multiple bpf
programs attached to the same networking device through **tc**, the order
will be first all bpf programs attached to tcx, netkit, then tc classes,
then all bpf programs attached to non clsact qdiscs, and finally all bpf
programs attached to root and clsact qdisc.
bpftool net attach *ATTACH_TYPE* *PROG* dev *NAME* [ overwrite | prepend ]
Attach bpf program *PROG* to network interface *NAME* with type specified
by *ATTACH_TYPE*.
*ATTACH_TYPE* can be of:
**xdp** - try native XDP and fallback to generic XDP if NIC driver does not support it;
**xdpgeneric** - Generic XDP. runs at generic XDP hook when packet already enters receive path as skb;
**xdpdrv** - Native XDP. runs earliest point in driver's receive path;
**xdpoffload** - Offload XDP. runs directly on NIC on each packet reception;
**tcx_ingress** - Ingress TCX. runs on ingress net traffic;
**tcx_egress** - Egress TCX. runs on egress net traffic;
For XDP-related attach types (**xdp**, **xdpgeneric**, **xdpdrv**,
**xdpoffload**), the **overwrite** option can be used to replace a
previously attached bpf program.
For **tcx_ingress** and **tcx_egress** attach types, the **prepend** option
can be used to attach the program at the beginning of the chain instead of
at the end.
bpftool net detach *ATTACH_TYPE* dev *NAME*
Detach bpf program attached to network interface *NAME* with type specified
by *ATTACH_TYPE*. To detach bpf program, same *ATTACH_TYPE* previously used
for attach must be specified.
bpftool net help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
EXAMPLES
========
| **# bpftool net**
::
xdp:
eth0(2) driver id 198
tc:
eth0(2) htb name prefix_matcher.o:[cls_prefix_matcher_htb] id 111727 act []
eth0(2) clsact/ingress fbflow_icmp id 130246 act []
eth0(2) clsact/egress prefix_matcher.o:[cls_prefix_matcher_clsact] id 111726
eth0(2) clsact/egress cls_fg_dscp id 108619 act []
eth0(2) clsact/egress fbflow_egress id 130245
|
| **# bpftool -jp net**
::
[{
"xdp": [{
"devname": "eth0",
"ifindex": 2,
"mode": "driver",
"id": 198
}
],
"tc": [{
"devname": "eth0",
"ifindex": 2,
"kind": "htb",
"name": "prefix_matcher.o:[cls_prefix_matcher_htb]",
"id": 111727,
"act": []
},{
"devname": "eth0",
"ifindex": 2,
"kind": "clsact/ingress",
"name": "fbflow_icmp",
"id": 130246,
"act": []
},{
"devname": "eth0",
"ifindex": 2,
"kind": "clsact/egress",
"name": "prefix_matcher.o:[cls_prefix_matcher_clsact]",
"id": 111726,
},{
"devname": "eth0",
"ifindex": 2,
"kind": "clsact/egress",
"name": "cls_fg_dscp",
"id": 108619,
"act": []
},{
"devname": "eth0",
"ifindex": 2,
"kind": "clsact/egress",
"name": "fbflow_egress",
"id": 130245,
}
]
}
]
|
| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
| **# bpftool net**
::
xdp:
enp6s0np0(4) driver id 16
|
| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
| **# bpftool net attach xdpdrv id 20 dev enp6s0np0 overwrite**
| **# bpftool net**
::
xdp:
enp6s0np0(4) driver id 20
|
| **# bpftool net attach xdpdrv id 16 dev enp6s0np0**
| **# bpftool net detach xdpdrv dev enp6s0np0**
| **# bpftool net**
::
xdp:
|
| **# bpftool net attach tcx_ingress name tc_prog dev lo**
| **# bpftool net**
|
::
tc:
lo(1) tcx/ingress tc_prog prog_id 29
|
| **# bpftool net attach tcx_ingress name tc_prog2 dev lo prepend**
| **# bpftool net**
|
::
tc:
lo(1) tcx/ingress tc_prog2 prog_id 30
lo(1) tcx/ingress tc_prog prog_id 29
|
| **# bpftool net attach tcx_ingress name tc_prog dev lo**
| **# bpftool net detach tcx_ingress dev lo**
| **# bpftool net**
|
::
tc:
@@ -0,0 +1,69 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-perf
================
-------------------------------------------------------------------------------
tool for inspection of perf related bpf prog attachments
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **perf** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| }
*COMMANDS* :=
{ **show** | **list** | **help** }
PERF COMMANDS
=============
| **bpftool** **perf** { **show** | **list** }
| **bpftool** **perf help**
DESCRIPTION
===========
bpftool perf { show | list }
List all raw_tracepoint, tracepoint, kprobe attachment in the system.
Output will start with process id and file descriptor in that process,
followed by bpf program id, attachment information, and attachment point.
The attachment point for raw_tracepoint/tracepoint is the trace probe name.
The attachment point for k[ret]probe is either symbol name and offset, or a
kernel virtual address. The attachment point for u[ret]probe is the file
name and the file offset.
bpftool perf help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
EXAMPLES
========
| **# bpftool perf**
::
pid 21711 fd 5: prog_id 5 kprobe func __x64_sys_write offset 0
pid 21765 fd 5: prog_id 7 kretprobe func __x64_sys_nanosleep offset 0
pid 21767 fd 5: prog_id 8 tracepoint sys_enter_nanosleep
pid 21800 fd 5: prog_id 9 uprobe filename /home/yhs/a.out offset 1159
|
| **# bpftool -j perf**
::
[{"pid":21711,"fd":5,"prog_id":5,"fd_type":"kprobe","func":"__x64_sys_write","offset":0}, \
{"pid":21765,"fd":5,"prog_id":7,"fd_type":"kretprobe","func":"__x64_sys_nanosleep","offset":0}, \
{"pid":21767,"fd":5,"prog_id":8,"fd_type":"tracepoint","tracepoint":"sys_enter_nanosleep"}, \
{"pid":21800,"fd":5,"prog_id":9,"fd_type":"uprobe","filename":"/home/yhs/a.out","offset":1159}]
@@ -0,0 +1,378 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-prog
================
-------------------------------------------------------------------------------
tool for inspection and simple manipulation of eBPF progs
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **prog** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| |
{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
{ **-L** | **--use-loader** } | [ { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> ] }
*COMMANDS* :=
{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
**loadall** | **help** }
PROG COMMANDS
=============
| **bpftool** **prog** { **show** | **list** } [*PROG*]
| **bpftool** **prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }]
| **bpftool** **prog dump jited** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }]
| **bpftool** **prog pin** *PROG* *FILE*
| **bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** { **idx** *IDX* | **name** *NAME* } *MAP*] [{ **offload_dev** | **xdpmeta_dev** } *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**] [**kernel_btf** *BTF_FILE*]
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog tracelog**
| **bpftool** **prog tracelog** [ { **stdout** | **stderr** } *PROG* ]
| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
| **bpftool** **prog profile** *PROG* [**duration** *DURATION*] *METRICs*
| **bpftool** **prog help**
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* | **name** *MAP_NAME* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
| *TYPE* := {
| **socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
| **tracepoint** | **raw_tracepoint** | **xdp** | **perf_event** | **cgroup/skb** |
| **cgroup/sock** | **cgroup/dev** | **lwt_in** | **lwt_out** | **lwt_xmit** |
| **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** |
| **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
| **cgroup/connect4** | **cgroup/connect6** | **cgroup/connect_unix** |
| **cgroup/getpeername4** | **cgroup/getpeername6** | **cgroup/getpeername_unix** |
| **cgroup/getsockname4** | **cgroup/getsockname6** | **cgroup/getsockname_unix** |
| **cgroup/sendmsg4** | **cgroup/sendmsg6** | **cgroup/sendmsg_unix** |
| **cgroup/recvmsg4** | **cgroup/recvmsg6** | **cgroup/recvmsg_unix** | **cgroup/sysctl** |
| **cgroup/getsockopt** | **cgroup/setsockopt** | **cgroup/sock_release** |
| **struct_ops** | **fentry** | **fexit** | **fsession** | **freplace** | **sk_lookup**
| }
| *ATTACH_TYPE* := {
| **sk_msg_verdict** | **sk_skb_verdict** | **sk_skb_stream_verdict** |
| **sk_skb_stream_parser** | **flow_dissector**
| }
| *METRICs* := {
| **cycles** | **instructions** | **l1d_loads** | **llc_misses** |
| **itlb_misses** | **dtlb_misses**
| }
DESCRIPTION
===========
bpftool prog { show | list } [*PROG*]
Show information about loaded programs. If *PROG* is specified show
information only about given programs, otherwise list all programs
currently loaded on the system. In case of **tag** or **name**, *PROG* may
match several programs which will all be shown.
Output will start with program ID followed by program type and zero or more
named attributes (depending on kernel version).
Since Linux 5.1 the kernel can collect statistics on BPF programs (such as
the total time spent running the program, and the number of times it was
run). If available, bpftool shows such statistics. However, the kernel does
not collect them by defaults, as it slightly impacts performance on each
program run. Activation or deactivation of the feature is performed via the
**kernel.bpf_stats_enabled** sysctl knob.
Since Linux 5.8 bpftool is able to discover information about processes
that hold open file descriptors (FDs) against BPF programs. On such kernels
bpftool will automatically emit this information as well.
bpftool prog dump xlated *PROG* [{ file *FILE* | [opcodes] [linum] [visual] }]
Dump eBPF instructions of the programs from the kernel. By default, eBPF
will be disassembled and printed to standard output in human-readable
format. In this case, **opcodes** controls if raw opcodes should be printed
as well.
In case of **tag** or **name**, *PROG* may match several programs which
will all be dumped. However, if **file** or **visual** is specified,
*PROG* must match a single program.
If **file** is specified, the binary image will instead be written to
*FILE*.
If **visual** is specified, control flow graph (CFG) will be built instead,
and eBPF instructions will be presented with CFG in DOT format, on standard
output.
If the programs have line_info available, the source line will be
displayed. If **linum** is specified, the filename, line number and line
column will also be displayed.
bpftool prog dump jited *PROG* [{ file *FILE* | [opcodes] [linum] }]
Dump jited image (host machine code) of the program.
If *FILE* is specified image will be written to a file, otherwise it will
be disassembled and printed to stdout. *PROG* must match a single program
when **file** is specified.
**opcodes** controls if raw opcodes will be printed.
If the prog has line_info available, the source line will be displayed. If
**linum** is specified, the filename, line number and line column will also
be displayed.
bpftool prog pin *PROG* *FILE*
Pin program *PROG* as *FILE*.
Note: *FILE* must be located in *bpffs* mount. It must not contain a dot
character ('.'), which is reserved for future extensions of *bpffs*.
bpftool prog { load | loadall } *OBJ* *PATH* [type *TYPE*] [map { idx *IDX* | name *NAME* } *MAP*] [{ offload_dev | xdpmeta_dev } *NAME*] [pinmaps *MAP_DIR*] [autoattach] [kernel_btf *BTF_FILE*]
Load bpf program(s) from binary *OBJ* and pin as *PATH*. **bpftool prog
load** pins only the first program from the *OBJ* as *PATH*. **bpftool prog
loadall** pins all programs from the *OBJ* under *PATH* directory. **type**
is optional, if not specified program type will be inferred from section
names. By default bpftool will create new maps as declared in the ELF
object being loaded. **map** parameter allows for the reuse of existing
maps. It can be specified multiple times, each time for a different map.
*IDX* refers to index of the map to be replaced in the ELF file counting
from 0, while *NAME* allows to replace a map by name. *MAP* specifies the
map to use, referring to it by **id** or through a **pinned** file. If
**offload_dev** *NAME* is specified program will be loaded onto given
networking device (offload). If **xdpmeta_dev** *NAME* is specified program
will become device-bound without offloading, this facilitates access to XDP
metadata. Optional **pinmaps** argument can be provided to pin all maps
under *MAP_DIR* directory.
If **autoattach** is specified program will be attached before pin. In that
case, only the link (representing the program attached to its hook) is
pinned, not the program as such, so the path won't show in **bpftool prog
show -f**, only show in **bpftool link show -f**. Also, this only works
when bpftool (libbpf) is able to infer all necessary information from the
object file, in particular, it's not supported for all program types. If a
program does not support autoattach, bpftool falls back to regular pinning
for that program instead.
The **kernel_btf** option allows specifying an external BTF file to replace
the system's own vmlinux BTF file for CO-RE relocations. Note that any
other feature relying on BTF (such as fentry/fexit programs, struct_ops)
requires the BTF file for the actual kernel running on the host, often
exposed at /sys/kernel/btf/vmlinux.
Note: *PATH* must be located in *bpffs* mount. It must not contain a dot
character ('.'), which is reserved for future extensions of *bpffs*.
bpftool prog attach *PROG* *ATTACH_TYPE* [*MAP*]
Attach bpf program *PROG* (with type specified by *ATTACH_TYPE*). Most
*ATTACH_TYPEs* require a *MAP* parameter, with the exception of
*flow_dissector* which is attached to current networking name space.
bpftool prog detach *PROG* *ATTACH_TYPE* [*MAP*]
Detach bpf program *PROG* (with type specified by *ATTACH_TYPE*). Most
*ATTACH_TYPEs* require a *MAP* parameter, with the exception of
*flow_dissector* which is detached from the current networking name space.
bpftool prog tracelog
Dump the trace pipe of the system to the console (stdout). Hit <Ctrl+C> to
stop printing. BPF programs can write to this trace pipe at runtime with
the **bpf_trace_printk**\ () helper. This should be used only for debugging
purposes. For streaming data from BPF programs to user space, one can use
perf events (see also **bpftool-map**\ (8)).
bpftool prog tracelog { stdout | stderr } *PROG*
Dump the BPF stream of the program. BPF programs can write to these streams
at runtime with the **bpf_stream_vprintk_impl**\ () kfunc. The kernel may write
error messages to the standard error stream. This facility should be used
only for debugging purposes.
bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*]
Run BPF program *PROG* in the kernel testing infrastructure for BPF,
meaning that the program works on the data and context provided by the
user, and not on actual packets or monitored functions etc. Return value
and duration for the test run are printed out to the console.
Input data is read from the *FILE* passed with **data_in**. If this *FILE*
is "**-**", input data is read from standard input. Input context, if any,
is read from *FILE* passed with **ctx_in**. Again, "**-**" can be used to
read from standard input, but only if standard input is not already in use
for input data. If a *FILE* is passed with **data_out**, output data is
written to that file. Similarly, output context is written to the *FILE*
passed with **ctx_out**. For both output flows, "**-**" can be used to
print to the standard output (as plain text, or JSON if relevant option was
passed). If output keywords are omitted, output data and context are
discarded. Keywords **data_size_out** and **ctx_size_out** are used to pass
the size (in bytes) for the output buffers to the kernel, although the
default of 32 kB should be more than enough for most cases.
Keyword **repeat** is used to indicate the number of consecutive runs to
perform. Note that output data and context printed to files correspond to
the last of those runs. The duration printed out at the end of the runs is
an average over all runs performed by the command.
Not all program types support test run. Among those which do, not all of
them can take the **ctx_in**/**ctx_out** arguments. bpftool does not
perform checks on program types.
bpftool prog profile *PROG* [duration *DURATION*] *METRICs*
Profile *METRICs* for bpf program *PROG* for *DURATION* seconds or until
user hits <Ctrl+C>. *DURATION* is optional. If *DURATION* is not specified,
the profiling will run up to **UINT_MAX** seconds.
bpftool prog help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
-f, --bpffs
When showing BPF programs, show file names of pinned programs.
-m, --mapcompat
Allow loading maps with unknown map definitions.
-n, --nomount
Do not automatically attempt to mount any virtual file system (such as
tracefs or BPF virtual file system) when necessary.
-L, --use-loader
Load program as a "loader" program. This is useful to debug the generation
of such programs. When this option is in use, bpftool attempts to load the
programs from the object file into the kernel, but does not pin them
(therefore, the *PATH* must not be provided).
When combined with the **-d**\ \|\ **--debug** option, additional debug
messages are generated, and the execution of the loader program will use
the **bpf_trace_printk**\ () helper to log each step of loading BTF,
creating the maps, and loading the programs (see **bpftool prog tracelog**
as a way to dump those messages).
-S, --sign
Enable signing of the BPF program before loading. This option must be
used with **-k** and **-i**. Using this flag implicitly enables
**--use-loader**.
-k <private_key.pem>
Path to the private key file in PEM format, required when signing.
-i <certificate.x509>
Path to the X.509 certificate file in PEM or DER format, required when
signing.
EXAMPLES
========
**# bpftool prog show**
::
10: xdp name some_prog tag 005a3d2123620c8b gpl run_time_ns 81632 run_cnt 10
loaded_at 2017-09-29T20:11:00+0000 uid 0
xlated 528B jited 370B memlock 4096B map_ids 10
pids systemd(1)
**# bpftool --json --pretty prog show**
::
[{
"id": 10,
"type": "xdp",
"tag": "005a3d2123620c8b",
"gpl_compatible": true,
"run_time_ns": 81632,
"run_cnt": 10,
"loaded_at": 1506715860,
"uid": 0,
"bytes_xlated": 528,
"jited": true,
"bytes_jited": 370,
"bytes_memlock": 4096,
"map_ids": [10
],
"pids": [{
"pid": 1,
"comm": "systemd"
}
]
}
]
|
| **# bpftool prog dump xlated id 10 file /tmp/t**
| **$ ls -l /tmp/t**
::
-rw------- 1 root root 560 Jul 22 01:42 /tmp/t
**# bpftool prog dump jited tag 005a3d2123620c8b**
::
0: push %rbp
1: mov %rsp,%rbp
2: sub $0x228,%rsp
3: sub $0x28,%rbp
4: mov %rbx,0x0(%rbp)
|
| **# mount -t bpf none /sys/fs/bpf/**
| **# bpftool prog pin id 10 /sys/fs/bpf/prog**
| **# bpftool prog load ./my_prog.o /sys/fs/bpf/prog2**
| **# ls -l /sys/fs/bpf/**
::
-rw------- 1 root root 0 Jul 22 01:43 prog
-rw------- 1 root root 0 Jul 22 01:44 prog2
**# bpftool prog dump jited pinned /sys/fs/bpf/prog opcodes**
::
0: push %rbp
55
1: mov %rsp,%rbp
48 89 e5
4: sub $0x228,%rsp
48 81 ec 28 02 00 00
b: sub $0x28,%rbp
48 83 ed 28
f: mov %rbx,0x0(%rbp)
48 89 5d 00
|
| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
| **# bpftool prog show pinned /sys/fs/bpf/xdp1**
::
9: xdp name xdp_prog1 tag 539ec6ce11b52f98 gpl
loaded_at 2018-06-25T16:17:31-0700 uid 0
xlated 488B jited 336B memlock 4096B map_ids 7
**# rm /sys/fs/bpf/xdp1**
|
| **# bpftool prog profile id 337 duration 10 cycles instructions llc_misses**
::
51397 run_cnt
40176203 cycles (83.05%)
42518139 instructions # 1.06 insns per cycle (83.39%)
123 llc_misses # 2.89 LLC misses per million insns (83.15%)
|
| Output below is for the trace logs.
| Run in separate terminals:
| **# bpftool prog tracelog**
| **# bpftool prog load -L -d file.o**
::
bpftool-620059 [004] d... 2634685.517903: bpf_trace_printk: btf_load size 665 r=5
bpftool-620059 [004] d... 2634685.517912: bpf_trace_printk: map_create sample_map idx 0 type 2 value_size 4 value_btf_id 0 r=6
bpftool-620059 [004] d... 2634685.517997: bpf_trace_printk: prog_load sample insn_cnt 13 r=7
bpftool-620059 [004] d... 2634685.517999: bpf_trace_printk: close(5) = 0
@@ -0,0 +1,91 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
==================
bpftool-struct_ops
==================
-------------------------------------------------------------------------------
tool to register/unregister/introspect BPF struct_ops
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **struct_ops** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| }
*COMMANDS* :=
{ **show** | **list** | **dump** | **register** | **unregister** | **help** }
STRUCT_OPS COMMANDS
===================
| **bpftool** **struct_ops { show | list }** [*STRUCT_OPS_MAP*]
| **bpftool** **struct_ops dump** [*STRUCT_OPS_MAP*]
| **bpftool** **struct_ops register** *OBJ* [*LINK_DIR*]
| **bpftool** **struct_ops unregister** *STRUCT_OPS_MAP*
| **bpftool** **struct_ops help**
|
| *STRUCT_OPS_MAP* := { **id** *STRUCT_OPS_MAP_ID* | **name** *STRUCT_OPS_MAP_NAME* }
| *OBJ* := /a/file/of/bpf_struct_ops.o
DESCRIPTION
===========
bpftool struct_ops { show | list } [*STRUCT_OPS_MAP*]
Show brief information about the struct_ops in the system. If
*STRUCT_OPS_MAP* is specified, it shows information only for the given
struct_ops. Otherwise, it lists all struct_ops currently existing in the
system.
Output will start with struct_ops map ID, followed by its map name and its
struct_ops's kernel type.
bpftool struct_ops dump [*STRUCT_OPS_MAP*]
Dump details information about the struct_ops in the system. If
*STRUCT_OPS_MAP* is specified, it dumps information only for the given
struct_ops. Otherwise, it dumps all struct_ops currently existing in the
system.
bpftool struct_ops register *OBJ* [*LINK_DIR*]
Register bpf struct_ops from *OBJ*. All struct_ops under the ELF section
".struct_ops" and ".struct_ops.link" will be registered to its kernel
subsystem. For each struct_ops in the ".struct_ops.link" section, a link
will be created. You can give *LINK_DIR* to provide a directory path where
these links will be pinned with the same name as their corresponding map
name.
bpftool struct_ops unregister *STRUCT_OPS_MAP*
Unregister the *STRUCT_OPS_MAP* from the kernel subsystem.
bpftool struct_ops help
Print short help message.
OPTIONS
=======
.. include:: common_options.rst
EXAMPLES
========
**# bpftool struct_ops show**
::
100: dctcp tcp_congestion_ops
105: cubic tcp_congestion_ops
**# bpftool struct_ops unregister id 105**
::
Unregistered tcp_congestion_ops cubic id 105
**# bpftool struct_ops register bpf_cubic.o**
::
Registered tcp_congestion_ops cubic id 110
@@ -0,0 +1,64 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
bpftool-token
================
-------------------------------------------------------------------------------
tool for inspection and simple manipulation of eBPF tokens
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] **token** *COMMAND*
*OPTIONS* := { |COMMON_OPTIONS| }
*COMMANDS* := { **show** | **list** | **help** }
TOKEN COMMANDS
===============
| **bpftool** **token** { **show** | **list** }
| **bpftool** **token help**
|
DESCRIPTION
===========
bpftool token { show | list }
List BPF token information for each *bpffs* mount point containing token
information on the system. Information include mount point path, allowed
**bpf**\ () system call commands, maps, programs, and attach types for the
token.
bpftool prog help
Print short help message.
OPTIONS
========
.. include:: common_options.rst
EXAMPLES
========
|
| **# mkdir -p /sys/fs/bpf/token**
| **# mount -t bpf bpffs /sys/fs/bpf/token** \
| **-o delegate_cmds=prog_load:map_create** \
| **-o delegate_progs=kprobe** \
| **-o delegate_attachs=xdp**
| **# bpftool token list**
::
token_info /sys/fs/bpf/token
allowed_cmds:
map_create prog_load
allowed_maps:
allowed_progs:
kprobe
allowed_attachs:
xdp
@@ -0,0 +1,70 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
================
BPFTOOL
================
-------------------------------------------------------------------------------
tool for inspection and simple manipulation of eBPF programs and maps
-------------------------------------------------------------------------------
:Manual section: 8
.. include:: substitutions.rst
SYNOPSIS
========
**bpftool** [*OPTIONS*] *OBJECT* { *COMMAND* | **help** }
**bpftool** **batch file** *FILE*
**bpftool** **version**
*OBJECT* := { **map** | **prog** | **link** | **cgroup** | **perf** | **net** | **feature** |
**btf** | **gen** | **struct_ops** | **iter** }
*OPTIONS* := { { **-V** | **--version** } | |COMMON_OPTIONS| }
*MAP-COMMANDS* :=
{ **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** |
**delete** | **pin** | **event_pipe** | **help** }
*PROG-COMMANDS* := { **show** | **list** | **dump jited** | **dump xlated** | **pin** |
**load** | **attach** | **detach** | **help** }
*LINK-COMMANDS* := { **show** | **list** | **pin** | **detach** | **help** }
*CGROUP-COMMANDS* := { **show** | **list** | **attach** | **detach** | **help** }
*PERF-COMMANDS* := { **show** | **list** | **help** }
*NET-COMMANDS* := { **show** | **list** | **help** }
*FEATURE-COMMANDS* := { **probe** | **help** }
*BTF-COMMANDS* := { **show** | **list** | **dump** | **help** }
*GEN-COMMANDS* := { **object** | **skeleton** | **min_core_btf** | **help** }
*STRUCT-OPS-COMMANDS* := { **show** | **list** | **dump** | **register** | **unregister** | **help** }
*ITER-COMMANDS* := { **pin** | **help** }
DESCRIPTION
===========
*bpftool* allows for inspection and simple modification of BPF objects on the
system.
Note that format of the output of all tools is not guaranteed to be stable and
should not be depended upon.
OPTIONS
=======
.. include:: common_options.rst
-m, --mapcompat
Allow loading maps with unknown map definitions.
-n, --nomount
Do not automatically attempt to mount any virtual file system (such as
tracefs or BPF virtual file system) when necessary.
@@ -0,0 +1,23 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-h, --help
Print short help message (similar to **bpftool help**).
-V, --version
Print bpftool's version number (similar to **bpftool version**), the number
of the libbpf version in use, and optional features that were included when
bpftool was compiled. Optional features include linking against LLVM or
libbfd to provide the disassembler for JIT-ted programs (**bpftool prog
dump jited**) and usage of BPF skeletons (some features like **bpftool prog
profile** or showing pids associated to BPF objects may rely on it).
-j, --json
Generate JSON output. For commands that cannot produce JSON, this option
has no effect.
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
-d, --debug
Print all logs available, even debug-level information. This includes logs
from libbpf as well as from the verifier, when attempting to load programs.
@@ -0,0 +1,3 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
.. |COMMON_OPTIONS| replace:: { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-d** | **--debug** }
+346
View File
@@ -0,0 +1,346 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
include ../../scripts/Makefile.include
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
BPF_DIR = $(srctree)/tools/lib/bpf
ifneq ($(OUTPUT),)
_OUTPUT := $(OUTPUT)
else
_OUTPUT := $(CURDIR)/
endif
BOOTSTRAP_OUTPUT := $(_OUTPUT)bootstrap/
LIBBPF_OUTPUT := $(_OUTPUT)libbpf/
LIBBPF_DESTDIR := $(LIBBPF_OUTPUT)
LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)include
LIBBPF_HDRS_DIR := $(LIBBPF_INCLUDE)/bpf
LIBBPF := $(LIBBPF_OUTPUT)libbpf.a
LIBBPF_BOOTSTRAP_OUTPUT := $(BOOTSTRAP_OUTPUT)libbpf/
LIBBPF_BOOTSTRAP_DESTDIR := $(LIBBPF_BOOTSTRAP_OUTPUT)
LIBBPF_BOOTSTRAP_INCLUDE := $(LIBBPF_BOOTSTRAP_DESTDIR)include
LIBBPF_BOOTSTRAP_HDRS_DIR := $(LIBBPF_BOOTSTRAP_INCLUDE)/bpf
LIBBPF_BOOTSTRAP := $(LIBBPF_BOOTSTRAP_OUTPUT)libbpf.a
# We need to copy hashmap.h, nlattr.h, relo_core.h and libbpf_internal.h
# which are not otherwise exported by libbpf, but still required by bpftool.
LIBBPF_INTERNAL_HDRS := $(addprefix $(LIBBPF_HDRS_DIR)/,hashmap.h nlattr.h relo_core.h libbpf_internal.h)
LIBBPF_BOOTSTRAP_INTERNAL_HDRS := $(addprefix $(LIBBPF_BOOTSTRAP_HDRS_DIR)/,hashmap.h relo_core.h libbpf_internal.h)
$(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT) $(LIBBPF_BOOTSTRAP_OUTPUT) $(LIBBPF_HDRS_DIR) $(LIBBPF_BOOTSTRAP_HDRS_DIR):
$(QUIET_MKDIR)mkdir -p $@
$(LIBBPF): $(wildcard $(BPF_DIR)/*.[ch] $(BPF_DIR)/Makefile) | $(LIBBPF_OUTPUT)
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) \
DESTDIR=$(LIBBPF_DESTDIR:/=) prefix= $(LIBBPF) install_headers
$(LIBBPF_INTERNAL_HDRS): $(LIBBPF_HDRS_DIR)/%.h: $(BPF_DIR)/%.h | $(LIBBPF_HDRS_DIR)
$(call QUIET_INSTALL, $@)
$(Q)install -m 644 -t $(LIBBPF_HDRS_DIR) $<
$(LIBBPF_BOOTSTRAP): $(wildcard $(BPF_DIR)/*.[ch] $(BPF_DIR)/Makefile) | $(LIBBPF_BOOTSTRAP_OUTPUT)
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_BOOTSTRAP_OUTPUT) \
DESTDIR=$(LIBBPF_BOOTSTRAP_DESTDIR:/=) prefix= \
ARCH= CROSS_COMPILE= CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" \
CFLAGS="$(LIBBPF_BOOTSTRAP_CFLAGS)" EXTRA_CFLAGS= $@ install_headers
$(LIBBPF_BOOTSTRAP_INTERNAL_HDRS): $(LIBBPF_BOOTSTRAP_HDRS_DIR)/%.h: $(BPF_DIR)/%.h | $(LIBBPF_BOOTSTRAP_HDRS_DIR)
$(call QUIET_INSTALL, $@)
$(Q)install -m 644 -t $(LIBBPF_BOOTSTRAP_HDRS_DIR) $<
$(LIBBPF)-clean: FORCE | $(LIBBPF_OUTPUT)
$(call QUIET_CLEAN, libbpf)
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) clean >/dev/null
$(LIBBPF_BOOTSTRAP)-clean: FORCE | $(LIBBPF_BOOTSTRAP_OUTPUT)
$(call QUIET_CLEAN, libbpf-bootstrap)
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_BOOTSTRAP_OUTPUT) clean >/dev/null
prefix ?= /usr/local
bash_compdir ?= /usr/share/bash-completion/completions
CFLAGS += -O2
CFLAGS += -W
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -Wformat-signedness
CFLAGS += -Wno-unused-parameter
CFLAGS += -Wno-missing-field-initializers
CFLAGS += $(filter-out -Wswitch-enum -Wnested-externs,$(EXTRA_WARNINGS))
CFLAGS += -DPACKAGE='"bpftool"' -D__EXPORTED_HEADERS__ \
-I$(or $(OUTPUT),.) \
-I$(LIBBPF_INCLUDE) \
-I$(srctree)/kernel/bpf/ \
-I$(srctree)/tools/include \
-I$(srctree)/tools/include/uapi
ifneq ($(BPFTOOL_VERSION),)
CFLAGS += -DBPFTOOL_VERSION='"$(BPFTOOL_VERSION)"'
endif
# This must be done before appending EXTRA_CFLAGS to CFLAGS to avoid
# including flags that are not applicable to the host compiler.
HOST_CFLAGS := $(subst -I$(LIBBPF_INCLUDE),-I$(LIBBPF_BOOTSTRAP_INCLUDE),\
$(subst $(CLANG_CROSS_FLAGS),,$(CFLAGS)))
HOST_CFLAGS += $(HOST_EXTRACFLAGS)
ifneq ($(EXTRA_CFLAGS),)
CFLAGS += $(EXTRA_CFLAGS)
endif
ifneq ($(EXTRA_LDFLAGS),)
LDFLAGS += $(EXTRA_LDFLAGS)
endif
HOST_LDFLAGS := $(LDFLAGS)
# Remove warnings for libbpf bootstrap build
LIBBPF_BOOTSTRAP_CFLAGS := $(filter-out -W -Wall -Wextra -Wformat -Wformat-signedness,$(HOST_CFLAGS))
INSTALL ?= install
RM ?= rm -f
FEATURE_USER = .bpftool
# Skip optional dependencies: LLVM (JIT disasm), libbfd (fallback
# disasm), libcrypto (program signing).
SKIP_LLVM ?=
SKIP_LIBBFD ?=
SKIP_CRYPTO ?=
ifneq ($(SKIP_CRYPTO),1)
CRYPTO_LIBS := -lcrypto
endif
ifeq ($(MAKECMDGOALS),bootstrap)
FEATURE_TESTS := libelf-zstd
FEATURE_DISPLAY :=
else
FEATURE_TESTS := clang-bpf-co-re
FEATURE_TESTS += llvm
FEATURE_TESTS += libcap
FEATURE_TESTS += libbfd
FEATURE_TESTS += libbfd-liberty
FEATURE_TESTS += libbfd-liberty-z
FEATURE_TESTS += disassembler-four-args
FEATURE_TESTS += disassembler-init-styled
FEATURE_TESTS += libelf-zstd
FEATURE_DISPLAY := clang-bpf-co-re
FEATURE_DISPLAY += llvm
FEATURE_DISPLAY += libcap
FEATURE_DISPLAY += libbfd
FEATURE_DISPLAY += libbfd-liberty
FEATURE_DISPLAY += libbfd-liberty-z
endif
check_feat := 1
NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall
ifdef MAKECMDGOALS
ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
check_feat := 0
endif
endif
ifeq ($(check_feat),1)
ifeq ($(FEATURES_DUMP),)
include $(srctree)/tools/build/Makefile.feature
else
include $(FEATURES_DUMP)
endif
endif
LIBS = $(LIBBPF) -lelf $(CRYPTO_LIBS) -lz
LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf $(CRYPTO_LIBS) -lz
ifeq ($(feature-libelf-zstd),1)
LIBS += -lzstd
LIBS_BOOTSTRAP += -lzstd
endif
ifeq ($(feature-libcap), 1)
CFLAGS += -DUSE_LIBCAP
LIBS += -lcap
endif
include $(wildcard $(OUTPUT)*.d)
all: $(OUTPUT)bpftool
SRCS := $(wildcard *.c)
ifeq ($(feature-llvm),1)
ifneq ($(SKIP_LLVM),1)
HAS_LLVM := 1
endif
endif
ifeq ($(HAS_LLVM),1)
CFLAGS += -DHAVE_LLVM_SUPPORT
LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets
# llvm-config always adds -D_GNU_SOURCE, however, it may already be in CFLAGS
# (e.g. when bpftool build is called from selftests build as selftests
# Makefile includes lib.mk which sets -D_GNU_SOURCE) which would cause
# compilation error due to redefinition. Let's filter it out here.
CFLAGS += $(filter-out -D_GNU_SOURCE,$(shell $(LLVM_CONFIG) --cflags))
LIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS))
ifeq ($(shell $(LLVM_CONFIG) --shared-mode),static)
LIBS += $(shell $(LLVM_CONFIG) --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
LIBS += -lstdc++
endif
LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags)
else
ifneq ($(SKIP_LIBBFD),1)
# Fall back on libbfd
ifeq ($(feature-libbfd),1)
LIBS += -lbfd -ldl -lopcodes
else ifeq ($(feature-libbfd-liberty),1)
LIBS += -lbfd -ldl -lopcodes -liberty
else ifeq ($(feature-libbfd-liberty-z),1)
LIBS += -lbfd -ldl -lopcodes -liberty -lz
endif
# If one of the above feature combinations is set, we support libbfd
ifneq ($(filter -lbfd,$(LIBS)),)
CFLAGS += -DHAVE_LIBBFD_SUPPORT
# Libbfd interface changed over time, figure out what we need
ifeq ($(feature-disassembler-four-args), 1)
CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
endif
ifeq ($(feature-disassembler-init-styled), 1)
CFLAGS += -DDISASM_INIT_STYLED
endif
endif
endif # SKIP_LIBBFD
endif
ifeq ($(filter -DHAVE_LLVM_SUPPORT -DHAVE_LIBBFD_SUPPORT,$(CFLAGS)),)
# No support for JIT disassembly
SRCS := $(filter-out jit_disasm.c,$(SRCS))
endif
ifeq ($(SKIP_CRYPTO),1)
CFLAGS += -DBPFTOOL_WITHOUT_CRYPTO
HOST_CFLAGS += -DBPFTOOL_WITHOUT_CRYPTO
SRCS := $(filter-out sign.c,$(SRCS))
endif
BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o $(if $(CRYPTO_LIBS),sign.o))
$(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP)
OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
$(OBJS): $(LIBBPF) $(LIBBPF_INTERNAL_HDRS)
VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
$(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
../../../vmlinux \
/sys/kernel/btf/vmlinux \
/boot/vmlinux-$(shell uname -r)
VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
bootstrap: $(BPFTOOL_BOOTSTRAP)
ifneq ($(VMLINUX_BTF)$(VMLINUX_H),)
ifeq ($(feature-clang-bpf-co-re),1)
BUILD_BPF_SKELS := 1
ifeq ($(VMLINUX_H),)
$(OUTPUT)vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL_BOOTSTRAP)
$(QUIET_GEN)$(BPFTOOL_BOOTSTRAP) btf dump file $< format c > $@
else
$(OUTPUT)vmlinux.h: $(VMLINUX_H)
$(Q)cp "$(VMLINUX_H)" $@
endif
$(OUTPUT)%.bpf.o: skeleton/%.bpf.c $(OUTPUT)vmlinux.h $(LIBBPF_BOOTSTRAP)
$(QUIET_CLANG)$(CLANG) \
-Wno-microsoft-anon-tag \
-fms-extensions \
-I$(or $(OUTPUT),.) \
-I$(srctree)/tools/include/uapi/ \
-I$(LIBBPF_BOOTSTRAP_INCLUDE) \
-g -O2 -Wall -fno-stack-protector \
--target=bpf -c $< -o $@
$(Q)$(LLVM_STRIP) -g $@
$(OUTPUT)%.skel.h: $(OUTPUT)%.bpf.o $(BPFTOOL_BOOTSTRAP)
$(QUIET_GEN)$(BPFTOOL_BOOTSTRAP) gen skeleton $< > $@
$(OUTPUT)prog.o: $(OUTPUT)profiler.skel.h
$(OUTPUT)pids.o: $(OUTPUT)pid_iter.skel.h
endif
endif
CFLAGS += $(if $(BUILD_BPF_SKELS),,-DBPFTOOL_WITHOUT_SKELETONS)
$(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
$(QUIET_CC)$(CC) $(CFLAGS) -c -MMD $< -o $@
$(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF_BOOTSTRAP)
$(QUIET_LINK)$(HOSTCC) $(HOST_CFLAGS) $(HOST_LDFLAGS) $(BOOTSTRAP_OBJS) $(LIBS_BOOTSTRAP) -o $@
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
$(BOOTSTRAP_OUTPUT)%.o: %.c $(LIBBPF_BOOTSTRAP_INTERNAL_HDRS) | $(BOOTSTRAP_OUTPUT)
$(QUIET_CC)$(HOSTCC) $(HOST_CFLAGS) -c -MMD $< -o $@
$(OUTPUT)%.o: %.c
$(QUIET_CC)$(CC) $(CFLAGS) -c -MMD $< -o $@
feature-detect-clean:
$(call QUIET_CLEAN, feature-detect)
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
clean: $(LIBBPF)-clean $(LIBBPF_BOOTSTRAP)-clean feature-detect-clean
$(call QUIET_CLEAN, bpftool)
$(Q)$(RM) -- $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
$(Q)$(RM) -- $(OUTPUT)*.skel.h $(OUTPUT)vmlinux.h
$(Q)$(RM) -r -- $(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT)
$(call QUIET_CLEAN, core-gen)
$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpftool
$(Q)$(RM) -r -- $(OUTPUT)feature/
install-bin: $(OUTPUT)bpftool
$(call QUIET_INSTALL, bpftool)
$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/sbin
$(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/sbin/bpftool
install: install-bin
$(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(bash_compdir)
$(Q)$(INSTALL) -m 0644 bash-completion/bpftool $(DESTDIR)$(bash_compdir)
uninstall:
$(call QUIET_UNINST, bpftool)
$(Q)$(RM) -- $(DESTDIR)$(prefix)/sbin/bpftool
$(Q)$(RM) -- $(DESTDIR)$(bash_compdir)/bpftool
doc:
$(call descend,Documentation)
doc-clean:
$(call descend,Documentation,clean)
doc-install:
$(call descend,Documentation,install)
doc-uninstall:
$(call descend,Documentation,uninstall)
FORCE:
.SECONDARY:
.PHONY: all FORCE bootstrap clean install-bin install uninstall
.PHONY: doc doc-clean doc-install doc-uninstall
.DEFAULT_GOAL := all
# Delete partially updated (corrupted) files on error
.DELETE_ON_ERROR:
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+906
View File
@@ -0,0 +1,906 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (c) 2018 Facebook */
#include <ctype.h>
#include <stdio.h> /* for (FILE *) used by json_writer */
#include <string.h>
#include <unistd.h>
#include <asm/byteorder.h>
#include <linux/bitops.h>
#include <linux/btf.h>
#include <linux/err.h>
#include <bpf/btf.h>
#include <bpf/bpf.h>
#include "json_writer.h"
#include "main.h"
#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
#define BITS_ROUNDUP_BYTES(bits) \
(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
__u8 bit_offset, const void *data);
static int btf_dump_func(const struct btf *btf, char *func_sig,
const struct btf_type *func_proto,
const struct btf_type *func, int pos, int size);
static int dump_prog_id_as_func_ptr(const struct btf_dumper *d,
const struct btf_type *func_proto,
__u32 prog_id)
{
const struct btf_type *func_type;
int prog_fd = -1, func_sig_len;
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
const char *prog_name = NULL;
struct btf *prog_btf = NULL;
struct bpf_func_info finfo = {};
__u32 finfo_rec_size;
char prog_str[1024];
int err;
/* Get the ptr's func_proto */
func_sig_len = btf_dump_func(d->btf, prog_str, func_proto, NULL, 0,
sizeof(prog_str));
if (func_sig_len == -1)
return -1;
if (!prog_id)
goto print;
/* Get the bpf_prog's name. Obtain from func_info. */
prog_fd = bpf_prog_get_fd_by_id(prog_id);
if (prog_fd < 0)
goto print;
err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
if (err)
goto print;
if (!info.btf_id || !info.nr_func_info)
goto print;
finfo_rec_size = info.func_info_rec_size;
memset(&info, 0, sizeof(info));
info.nr_func_info = 1;
info.func_info_rec_size = finfo_rec_size;
info.func_info = ptr_to_u64(&finfo);
err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
if (err)
goto print;
prog_btf = btf__load_from_kernel_by_id(info.btf_id);
if (!prog_btf)
goto print;
func_type = btf__type_by_id(prog_btf, finfo.type_id);
if (!func_type || !btf_is_func(func_type))
goto print;
prog_name = btf__name_by_offset(prog_btf, func_type->name_off);
print:
if (!prog_id)
snprintf(&prog_str[func_sig_len],
sizeof(prog_str) - func_sig_len, " 0");
else if (prog_name)
snprintf(&prog_str[func_sig_len],
sizeof(prog_str) - func_sig_len,
" %s/prog_id:%u", prog_name, prog_id);
else
snprintf(&prog_str[func_sig_len],
sizeof(prog_str) - func_sig_len,
" <unknown_prog_name>/prog_id:%u", prog_id);
prog_str[sizeof(prog_str) - 1] = '\0';
jsonw_string(d->jw, prog_str);
btf__free(prog_btf);
if (prog_fd >= 0)
close(prog_fd);
return 0;
}
static void btf_dumper_ptr(const struct btf_dumper *d,
const struct btf_type *t,
const void *data)
{
unsigned long value = *(unsigned long *)data;
const struct btf_type *ptr_type;
__s32 ptr_type_id;
if (!d->prog_id_as_func_ptr || value > UINT32_MAX)
goto print_ptr_value;
ptr_type_id = btf__resolve_type(d->btf, t->type);
if (ptr_type_id < 0)
goto print_ptr_value;
ptr_type = btf__type_by_id(d->btf, ptr_type_id);
if (!ptr_type || !btf_is_func_proto(ptr_type))
goto print_ptr_value;
if (!dump_prog_id_as_func_ptr(d, ptr_type, value))
return;
print_ptr_value:
if (d->is_plain_text)
jsonw_printf(d->jw, "\"%p\"", (void *)value);
else
jsonw_printf(d->jw, "%lu", value);
}
static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
__u8 bit_offset, const void *data)
{
int actual_type_id;
actual_type_id = btf__resolve_type(d->btf, type_id);
if (actual_type_id < 0)
return actual_type_id;
return btf_dumper_do_type(d, actual_type_id, bit_offset, data);
}
static int btf_dumper_enum(const struct btf_dumper *d,
const struct btf_type *t,
const void *data)
{
const struct btf_enum *enums = btf_enum(t);
__s64 value;
__u32 i;
switch (t->size) {
case 8:
value = *(__s64 *)data;
break;
case 4:
value = *(__s32 *)data;
break;
case 2:
value = *(__s16 *)data;
break;
case 1:
value = *(__s8 *)data;
break;
default:
return -EINVAL;
}
for (i = 0; i < btf_vlen(t); i++) {
if (value == enums[i].val) {
jsonw_string(d->jw,
btf__name_by_offset(d->btf,
enums[i].name_off));
return 0;
}
}
jsonw_int(d->jw, value);
return 0;
}
static int btf_dumper_enum64(const struct btf_dumper *d,
const struct btf_type *t,
const void *data)
{
const struct btf_enum64 *enums = btf_enum64(t);
__u32 val_lo32, val_hi32;
__u64 value;
__u32 i;
value = *(__u64 *)data;
val_lo32 = (__u32)value;
val_hi32 = value >> 32;
for (i = 0; i < btf_vlen(t); i++) {
if (val_lo32 == enums[i].val_lo32 && val_hi32 == enums[i].val_hi32) {
jsonw_string(d->jw,
btf__name_by_offset(d->btf,
enums[i].name_off));
return 0;
}
}
jsonw_int(d->jw, value);
return 0;
}
static bool is_str_array(const struct btf *btf, const struct btf_array *arr,
const char *s)
{
const struct btf_type *elem_type;
const char *end_s;
if (!arr->nelems)
return false;
elem_type = btf__type_by_id(btf, arr->type);
/* Not skipping typedef. typedef to char does not count as
* a string now.
*/
while (elem_type && btf_is_mod(elem_type))
elem_type = btf__type_by_id(btf, elem_type->type);
if (!elem_type || !btf_is_int(elem_type) || elem_type->size != 1)
return false;
if (btf_int_encoding(elem_type) != BTF_INT_CHAR &&
strcmp("char", btf__name_by_offset(btf, elem_type->name_off)))
return false;
end_s = s + arr->nelems;
while (s < end_s) {
if (!*s)
return true;
if (*s <= 0x1f || *s >= 0x7f)
return false;
s++;
}
/* '\0' is not found */
return false;
}
static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
const struct btf_type *t = btf__type_by_id(d->btf, type_id);
struct btf_array *arr = (struct btf_array *)(t + 1);
long long elem_size;
int ret = 0;
__u32 i;
if (is_str_array(d->btf, arr, data)) {
jsonw_string(d->jw, data);
return 0;
}
elem_size = btf__resolve_size(d->btf, arr->type);
if (elem_size < 0)
return elem_size;
jsonw_start_array(d->jw);
for (i = 0; i < arr->nelems; i++) {
ret = btf_dumper_do_type(d, arr->type, 0,
data + i * elem_size);
if (ret)
break;
}
jsonw_end_array(d->jw);
return ret;
}
static void btf_int128_print(json_writer_t *jw, const void *data,
bool is_plain_text)
{
/* data points to a __int128 number.
* Suppose
* int128_num = *(__int128 *)data;
* The below formulas shows what upper_num and lower_num represents:
* upper_num = int128_num >> 64;
* lower_num = int128_num & 0xffffffffFFFFFFFFULL;
*/
__u64 upper_num, lower_num;
#ifdef __BIG_ENDIAN_BITFIELD
upper_num = *(__u64 *)data;
lower_num = *(__u64 *)(data + 8);
#else
upper_num = *(__u64 *)(data + 8);
lower_num = *(__u64 *)data;
#endif
if (is_plain_text) {
if (upper_num == 0)
jsonw_printf(jw, "0x%llx", lower_num);
else
jsonw_printf(jw, "0x%llx%016llx", upper_num, lower_num);
} else {
if (upper_num == 0)
jsonw_printf(jw, "\"0x%llx\"", lower_num);
else
jsonw_printf(jw, "\"0x%llx%016llx\"", upper_num, lower_num);
}
}
static void btf_int128_shift(__u64 *print_num, __u16 left_shift_bits,
__u16 right_shift_bits)
{
__u64 upper_num, lower_num;
#ifdef __BIG_ENDIAN_BITFIELD
upper_num = print_num[0];
lower_num = print_num[1];
#else
upper_num = print_num[1];
lower_num = print_num[0];
#endif
/* shake out un-needed bits by shift/or operations */
if (left_shift_bits >= 64) {
upper_num = lower_num << (left_shift_bits - 64);
lower_num = 0;
} else {
upper_num = (upper_num << left_shift_bits) |
(lower_num >> (64 - left_shift_bits));
lower_num = lower_num << left_shift_bits;
}
if (right_shift_bits >= 64) {
lower_num = upper_num >> (right_shift_bits - 64);
upper_num = 0;
} else {
lower_num = (lower_num >> right_shift_bits) |
(upper_num << (64 - right_shift_bits));
upper_num = upper_num >> right_shift_bits;
}
#ifdef __BIG_ENDIAN_BITFIELD
print_num[0] = upper_num;
print_num[1] = lower_num;
#else
print_num[0] = lower_num;
print_num[1] = upper_num;
#endif
}
static void btf_dumper_bitfield(__u32 nr_bits, __u8 bit_offset,
const void *data, json_writer_t *jw,
bool is_plain_text)
{
int left_shift_bits, right_shift_bits;
__u64 print_num[2] = {};
int bytes_to_copy;
int bits_to_copy;
bits_to_copy = bit_offset + nr_bits;
bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
memcpy(print_num, data, bytes_to_copy);
#if defined(__BIG_ENDIAN_BITFIELD)
left_shift_bits = bit_offset;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
left_shift_bits = 128 - bits_to_copy;
#else
#error neither big nor little endian
#endif
right_shift_bits = 128 - nr_bits;
btf_int128_shift(print_num, left_shift_bits, right_shift_bits);
btf_int128_print(jw, print_num, is_plain_text);
}
static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
const void *data, json_writer_t *jw,
bool is_plain_text)
{
int nr_bits = BTF_INT_BITS(int_type);
int total_bits_offset;
/* bits_offset is at most 7.
* BTF_INT_OFFSET() cannot exceed 128 bits.
*/
total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
btf_dumper_bitfield(nr_bits, bit_offset, data, jw,
is_plain_text);
}
static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
const void *data, json_writer_t *jw,
bool is_plain_text)
{
__u32 *int_type;
__u32 nr_bits;
int_type = (__u32 *)(t + 1);
nr_bits = BTF_INT_BITS(*int_type);
/* if this is bit field */
if (bit_offset || BTF_INT_OFFSET(*int_type) ||
BITS_PER_BYTE_MASKED(nr_bits)) {
btf_dumper_int_bits(*int_type, bit_offset, data, jw,
is_plain_text);
return 0;
}
if (nr_bits == 128) {
btf_int128_print(jw, data, is_plain_text);
return 0;
}
switch (BTF_INT_ENCODING(*int_type)) {
case 0:
if (BTF_INT_BITS(*int_type) == 64)
jsonw_printf(jw, "%llu", *(__u64 *)data);
else if (BTF_INT_BITS(*int_type) == 32)
jsonw_printf(jw, "%u", *(__u32 *)data);
else if (BTF_INT_BITS(*int_type) == 16)
jsonw_printf(jw, "%hu", *(__u16 *)data);
else if (BTF_INT_BITS(*int_type) == 8)
jsonw_printf(jw, "%hhu", *(__u8 *)data);
else
btf_dumper_int_bits(*int_type, bit_offset, data, jw,
is_plain_text);
break;
case BTF_INT_SIGNED:
if (BTF_INT_BITS(*int_type) == 64)
jsonw_printf(jw, "%lld", *(long long *)data);
else if (BTF_INT_BITS(*int_type) == 32)
jsonw_printf(jw, "%d", *(int *)data);
else if (BTF_INT_BITS(*int_type) == 16)
jsonw_printf(jw, "%hd", *(short *)data);
else if (BTF_INT_BITS(*int_type) == 8)
jsonw_printf(jw, "%hhd", *(char *)data);
else
btf_dumper_int_bits(*int_type, bit_offset, data, jw,
is_plain_text);
break;
case BTF_INT_CHAR:
if (isprint(*(char *)data))
jsonw_printf(jw, "\"%c\"", *(char *)data);
else
if (is_plain_text)
jsonw_printf(jw, "0x%hhx", *(char *)data);
else
jsonw_printf(jw, "\"\\u00%02hhx\"",
*(char *)data);
break;
case BTF_INT_BOOL:
jsonw_bool(jw, *(bool *)data);
break;
default:
/* shouldn't happen */
return -EINVAL;
}
return 0;
}
static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
const struct btf_type *t;
struct btf_member *m;
const void *data_off;
int kind_flag;
int ret = 0;
int i, vlen;
t = btf__type_by_id(d->btf, type_id);
if (!t)
return -EINVAL;
kind_flag = BTF_INFO_KFLAG(t->info);
vlen = BTF_INFO_VLEN(t->info);
jsonw_start_object(d->jw);
m = (struct btf_member *)(t + 1);
for (i = 0; i < vlen; i++) {
__u32 bit_offset = m[i].offset;
__u32 bitfield_size = 0;
if (kind_flag) {
bitfield_size = BTF_MEMBER_BITFIELD_SIZE(bit_offset);
bit_offset = BTF_MEMBER_BIT_OFFSET(bit_offset);
}
jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
data_off = data + BITS_ROUNDDOWN_BYTES(bit_offset);
if (bitfield_size) {
btf_dumper_bitfield(bitfield_size,
BITS_PER_BYTE_MASKED(bit_offset),
data_off, d->jw, d->is_plain_text);
} else {
ret = btf_dumper_do_type(d, m[i].type,
BITS_PER_BYTE_MASKED(bit_offset),
data_off);
if (ret)
break;
}
}
jsonw_end_object(d->jw);
return ret;
}
static int btf_dumper_var(const struct btf_dumper *d, __u32 type_id,
__u8 bit_offset, const void *data)
{
const struct btf_type *t = btf__type_by_id(d->btf, type_id);
int ret;
jsonw_start_object(d->jw);
jsonw_name(d->jw, btf__name_by_offset(d->btf, t->name_off));
ret = btf_dumper_do_type(d, t->type, bit_offset, data);
jsonw_end_object(d->jw);
return ret;
}
static int btf_dumper_datasec(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
struct btf_var_secinfo *vsi;
const struct btf_type *t;
int ret = 0, i, vlen;
t = btf__type_by_id(d->btf, type_id);
if (!t)
return -EINVAL;
vlen = BTF_INFO_VLEN(t->info);
vsi = (struct btf_var_secinfo *)(t + 1);
jsonw_start_object(d->jw);
jsonw_name(d->jw, btf__name_by_offset(d->btf, t->name_off));
jsonw_start_array(d->jw);
for (i = 0; i < vlen; i++) {
ret = btf_dumper_do_type(d, vsi[i].type, 0, data + vsi[i].offset);
if (ret)
break;
}
jsonw_end_array(d->jw);
jsonw_end_object(d->jw);
return ret;
}
static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
__u8 bit_offset, const void *data)
{
const struct btf_type *t = btf__type_by_id(d->btf, type_id);
switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_INT:
return btf_dumper_int(t, bit_offset, data, d->jw,
d->is_plain_text);
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
return btf_dumper_struct(d, type_id, data);
case BTF_KIND_ARRAY:
return btf_dumper_array(d, type_id, data);
case BTF_KIND_ENUM:
return btf_dumper_enum(d, t, data);
case BTF_KIND_ENUM64:
return btf_dumper_enum64(d, t, data);
case BTF_KIND_PTR:
btf_dumper_ptr(d, t, data);
return 0;
case BTF_KIND_UNKN:
jsonw_printf(d->jw, "(unknown)");
return 0;
case BTF_KIND_FWD:
/* map key or value can't be forward */
jsonw_printf(d->jw, "(fwd-kind-invalid)");
return -EINVAL;
case BTF_KIND_TYPEDEF:
case BTF_KIND_VOLATILE:
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
return btf_dumper_modifier(d, type_id, bit_offset, data);
case BTF_KIND_VAR:
return btf_dumper_var(d, type_id, bit_offset, data);
case BTF_KIND_DATASEC:
return btf_dumper_datasec(d, type_id, data);
default:
jsonw_printf(d->jw, "(unsupported-kind)");
return -EINVAL;
}
}
int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
return btf_dumper_do_type(d, type_id, 0, data);
}
#define BTF_PRINT_ARG(...) \
do { \
pos += snprintf(func_sig + pos, size - pos, \
__VA_ARGS__); \
if (pos >= size) \
return -1; \
} while (0)
#define BTF_PRINT_TYPE(type) \
do { \
pos = __btf_dumper_type_only(btf, type, func_sig, \
pos, size); \
if (pos == -1) \
return -1; \
} while (0)
static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
char *func_sig, int pos, int size)
{
const struct btf_type *proto_type;
const struct btf_array *array;
const struct btf_var *var;
const struct btf_type *t;
if (!type_id) {
BTF_PRINT_ARG("void ");
return pos;
}
t = btf__type_by_id(btf, type_id);
switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_INT:
case BTF_KIND_TYPEDEF:
case BTF_KIND_FLOAT:
BTF_PRINT_ARG("%s ", btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_STRUCT:
BTF_PRINT_ARG("struct %s ",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_UNION:
BTF_PRINT_ARG("union %s ",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
BTF_PRINT_ARG("enum %s ",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_ARRAY:
array = (struct btf_array *)(t + 1);
BTF_PRINT_TYPE(array->type);
BTF_PRINT_ARG("[%u]", array->nelems);
break;
case BTF_KIND_PTR:
BTF_PRINT_TYPE(t->type);
BTF_PRINT_ARG("* ");
break;
case BTF_KIND_FWD:
BTF_PRINT_ARG("%s %s ",
BTF_INFO_KFLAG(t->info) ? "union" : "struct",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_VOLATILE:
BTF_PRINT_ARG("volatile ");
BTF_PRINT_TYPE(t->type);
break;
case BTF_KIND_CONST:
BTF_PRINT_ARG("const ");
BTF_PRINT_TYPE(t->type);
break;
case BTF_KIND_RESTRICT:
BTF_PRINT_ARG("restrict ");
BTF_PRINT_TYPE(t->type);
break;
case BTF_KIND_FUNC_PROTO:
pos = btf_dump_func(btf, func_sig, t, NULL, pos, size);
if (pos == -1)
return -1;
break;
case BTF_KIND_FUNC:
proto_type = btf__type_by_id(btf, t->type);
pos = btf_dump_func(btf, func_sig, proto_type, t, pos, size);
if (pos == -1)
return -1;
break;
case BTF_KIND_VAR:
var = (struct btf_var *)(t + 1);
if (var->linkage == BTF_VAR_STATIC)
BTF_PRINT_ARG("static ");
BTF_PRINT_TYPE(t->type);
BTF_PRINT_ARG(" %s",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_DATASEC:
BTF_PRINT_ARG("section (\"%s\") ",
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_UNKN:
default:
return -1;
}
return pos;
}
static int btf_dump_func(const struct btf *btf, char *func_sig,
const struct btf_type *func_proto,
const struct btf_type *func, int pos, int size)
{
int i, vlen;
BTF_PRINT_TYPE(func_proto->type);
if (func)
BTF_PRINT_ARG("%s(", btf__name_by_offset(btf, func->name_off));
else
BTF_PRINT_ARG("(");
vlen = BTF_INFO_VLEN(func_proto->info);
for (i = 0; i < vlen; i++) {
struct btf_param *arg = &((struct btf_param *)(func_proto + 1))[i];
if (i)
BTF_PRINT_ARG(", ");
if (arg->type) {
BTF_PRINT_TYPE(arg->type);
if (arg->name_off)
BTF_PRINT_ARG("%s",
btf__name_by_offset(btf, arg->name_off));
else if (pos && func_sig[pos - 1] == ' ')
/* Remove unnecessary space for
* FUNC_PROTO that does not have
* arg->name_off
*/
func_sig[--pos] = '\0';
} else {
BTF_PRINT_ARG("...");
}
}
BTF_PRINT_ARG(")");
return pos;
}
void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig,
int size)
{
int err;
func_sig[0] = '\0';
if (!btf)
return;
err = __btf_dumper_type_only(btf, type_id, func_sig, 0, size);
if (err < 0)
func_sig[0] = '\0';
}
static const char *ltrim(const char *s)
{
while (isspace(*s))
s++;
return s;
}
void btf_dump_linfo_plain(const struct btf *btf,
const struct bpf_line_info *linfo,
const char *prefix, bool linum)
{
const char *line = btf__name_by_offset(btf, linfo->line_off);
if (!line)
return;
line = ltrim(line);
if (!prefix)
prefix = "";
if (linum) {
const char *file = btf__name_by_offset(btf, linfo->file_name_off);
/* More forgiving on file because linum option is
* expected to provide more info than the already
* available src line.
*/
if (!file)
file = "";
printf("%s%s [file:%s line_num:%u line_col:%u]\n",
prefix, line, file,
BPF_LINE_INFO_LINE_NUM(linfo->line_col),
BPF_LINE_INFO_LINE_COL(linfo->line_col));
} else {
printf("%s%s\n", prefix, line);
}
}
void btf_dump_linfo_json(const struct btf *btf,
const struct bpf_line_info *linfo, bool linum)
{
const char *line = btf__name_by_offset(btf, linfo->line_off);
if (line)
jsonw_string_field(json_wtr, "src", ltrim(line));
if (linum) {
const char *file = btf__name_by_offset(btf, linfo->file_name_off);
if (file)
jsonw_string_field(json_wtr, "file", file);
if (BPF_LINE_INFO_LINE_NUM(linfo->line_col))
jsonw_int_field(json_wtr, "line_num",
BPF_LINE_INFO_LINE_NUM(linfo->line_col));
if (BPF_LINE_INFO_LINE_COL(linfo->line_col))
jsonw_int_field(json_wtr, "line_col",
BPF_LINE_INFO_LINE_COL(linfo->line_col));
}
}
static void dotlabel_puts(const char *s)
{
for (; *s; ++s) {
switch (*s) {
case '\\':
case '"':
case '{':
case '}':
case '<':
case '>':
case '|':
case ' ':
putchar('\\');
fallthrough;
default:
putchar(*s);
}
}
}
static const char *shorten_path(const char *path)
{
const unsigned int MAX_PATH_LEN = 32;
size_t len = strlen(path);
const char *shortpath;
if (len <= MAX_PATH_LEN)
return path;
/* Search for last '/' under the MAX_PATH_LEN limit */
shortpath = strchr(path + len - MAX_PATH_LEN, '/');
if (shortpath) {
if (shortpath < path + strlen("..."))
/* We removed a very short prefix, e.g. "/w", and we'll
* make the path longer by prefixing with the ellipsis.
* Not worth it, keep initial path.
*/
return path;
return shortpath;
}
/* File base name length is > MAX_PATH_LEN, search for last '/' */
shortpath = strrchr(path, '/');
if (shortpath)
return shortpath;
return path;
}
void btf_dump_linfo_dotlabel(const struct btf *btf,
const struct bpf_line_info *linfo, bool linum)
{
const char *line = btf__name_by_offset(btf, linfo->line_off);
if (!line || !strlen(line))
return;
line = ltrim(line);
if (linum) {
const char *file = btf__name_by_offset(btf, linfo->file_name_off);
const char *shortfile;
/* More forgiving on file because linum option is
* expected to provide more info than the already
* available src line.
*/
if (!file)
shortfile = "";
else
shortfile = shorten_path(file);
printf("; [%s", shortfile > file ? "..." : "");
dotlabel_puts(shortfile);
printf(" line:%u col:%u]\\l\\\n",
BPF_LINE_INFO_LINE_NUM(linfo->line_col),
BPF_LINE_INFO_LINE_COL(linfo->line_col));
}
printf("; ");
dotlabel_puts(line);
printf("\\l\\\n");
}
+489
View File
@@ -0,0 +1,489 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2018 Netronome Systems, Inc. */
#include <linux/list.h>
#include <stdlib.h>
#include <string.h>
#include "cfg.h"
#include "main.h"
#include "xlated_dumper.h"
struct cfg {
struct list_head funcs;
int func_num;
};
struct func_node {
struct list_head l;
struct list_head bbs;
struct bpf_insn *start;
struct bpf_insn *end;
int idx;
int bb_num;
};
struct bb_node {
struct list_head l;
struct list_head e_prevs;
struct list_head e_succs;
struct bpf_insn *head;
struct bpf_insn *tail;
int idx;
};
#define EDGE_FLAG_EMPTY 0x0
#define EDGE_FLAG_FALLTHROUGH 0x1
#define EDGE_FLAG_JUMP 0x2
struct edge_node {
struct list_head l;
struct bb_node *src;
struct bb_node *dst;
int flags;
};
#define ENTRY_BLOCK_INDEX 0
#define EXIT_BLOCK_INDEX 1
#define NUM_FIXED_BLOCKS 2
#define func_prev(func) list_prev_entry(func, l)
#define func_next(func) list_next_entry(func, l)
#define bb_prev(bb) list_prev_entry(bb, l)
#define bb_next(bb) list_next_entry(bb, l)
#define entry_bb(func) func_first_bb(func)
#define exit_bb(func) func_last_bb(func)
#define cfg_first_func(cfg) \
list_first_entry(&cfg->funcs, struct func_node, l)
#define cfg_last_func(cfg) \
list_last_entry(&cfg->funcs, struct func_node, l)
#define func_first_bb(func) \
list_first_entry(&func->bbs, struct bb_node, l)
#define func_last_bb(func) \
list_last_entry(&func->bbs, struct bb_node, l)
static struct func_node *cfg_append_func(struct cfg *cfg, struct bpf_insn *insn)
{
struct func_node *new_func, *func;
list_for_each_entry(func, &cfg->funcs, l) {
if (func->start == insn)
return func;
else if (func->start > insn)
break;
}
func = func_prev(func);
new_func = calloc(1, sizeof(*new_func));
if (!new_func) {
p_err("OOM when allocating FUNC node");
return NULL;
}
new_func->start = insn;
new_func->idx = cfg->func_num;
list_add(&new_func->l, &func->l);
cfg->func_num++;
return new_func;
}
static struct bb_node *func_append_bb(struct func_node *func,
struct bpf_insn *insn)
{
struct bb_node *new_bb, *bb;
list_for_each_entry(bb, &func->bbs, l) {
if (bb->head == insn)
return bb;
else if (bb->head > insn)
break;
}
bb = bb_prev(bb);
new_bb = calloc(1, sizeof(*new_bb));
if (!new_bb) {
p_err("OOM when allocating BB node");
return NULL;
}
new_bb->head = insn;
INIT_LIST_HEAD(&new_bb->e_prevs);
INIT_LIST_HEAD(&new_bb->e_succs);
list_add(&new_bb->l, &bb->l);
return new_bb;
}
static struct bb_node *func_insert_dummy_bb(struct list_head *after)
{
struct bb_node *bb;
bb = calloc(1, sizeof(*bb));
if (!bb) {
p_err("OOM when allocating BB node");
return NULL;
}
INIT_LIST_HEAD(&bb->e_prevs);
INIT_LIST_HEAD(&bb->e_succs);
list_add(&bb->l, after);
return bb;
}
static bool cfg_partition_funcs(struct cfg *cfg, struct bpf_insn *cur,
struct bpf_insn *end)
{
struct func_node *func, *last_func;
func = cfg_append_func(cfg, cur);
if (!func)
return true;
for (; cur < end; cur++) {
if (cur->code != (BPF_JMP | BPF_CALL))
continue;
if (cur->src_reg != BPF_PSEUDO_CALL)
continue;
func = cfg_append_func(cfg, cur + cur->off + 1);
if (!func)
return true;
}
last_func = cfg_last_func(cfg);
last_func->end = end - 1;
func = cfg_first_func(cfg);
list_for_each_entry_from(func, &last_func->l, l) {
func->end = func_next(func)->start - 1;
}
return false;
}
static bool is_jmp_insn(__u8 code)
{
return BPF_CLASS(code) == BPF_JMP || BPF_CLASS(code) == BPF_JMP32;
}
static bool func_partition_bb_head(struct func_node *func)
{
struct bpf_insn *cur, *end;
struct bb_node *bb;
cur = func->start;
end = func->end;
INIT_LIST_HEAD(&func->bbs);
bb = func_append_bb(func, cur);
if (!bb)
return true;
for (; cur <= end; cur++) {
if (is_jmp_insn(cur->code)) {
__u8 opcode = BPF_OP(cur->code);
if (opcode == BPF_EXIT || opcode == BPF_CALL)
continue;
bb = func_append_bb(func, cur + cur->off + 1);
if (!bb)
return true;
if (opcode != BPF_JA) {
bb = func_append_bb(func, cur + 1);
if (!bb)
return true;
}
}
}
return false;
}
static void func_partition_bb_tail(struct func_node *func)
{
unsigned int bb_idx = NUM_FIXED_BLOCKS;
struct bb_node *bb, *last;
last = func_last_bb(func);
last->tail = func->end;
bb = func_first_bb(func);
list_for_each_entry_from(bb, &last->l, l) {
bb->tail = bb_next(bb)->head - 1;
bb->idx = bb_idx++;
}
last->idx = bb_idx++;
func->bb_num = bb_idx;
}
static bool func_add_special_bb(struct func_node *func)
{
struct bb_node *bb;
bb = func_insert_dummy_bb(&func->bbs);
if (!bb)
return true;
bb->idx = ENTRY_BLOCK_INDEX;
bb = func_insert_dummy_bb(&func_last_bb(func)->l);
if (!bb)
return true;
bb->idx = EXIT_BLOCK_INDEX;
return false;
}
static bool func_partition_bb(struct func_node *func)
{
if (func_partition_bb_head(func))
return true;
func_partition_bb_tail(func);
return false;
}
static struct bb_node *func_search_bb_with_head(struct func_node *func,
struct bpf_insn *insn)
{
struct bb_node *bb;
list_for_each_entry(bb, &func->bbs, l) {
if (bb->head == insn)
return bb;
}
return NULL;
}
static struct edge_node *new_edge(struct bb_node *src, struct bb_node *dst,
int flags)
{
struct edge_node *e;
e = calloc(1, sizeof(*e));
if (!e) {
p_err("OOM when allocating edge node");
return NULL;
}
if (src)
e->src = src;
if (dst)
e->dst = dst;
e->flags |= flags;
return e;
}
static bool func_add_bb_edges(struct func_node *func)
{
struct bpf_insn *insn;
struct edge_node *e;
struct bb_node *bb;
bb = entry_bb(func);
e = new_edge(bb, bb_next(bb), EDGE_FLAG_FALLTHROUGH);
if (!e)
return true;
list_add_tail(&e->l, &bb->e_succs);
bb = exit_bb(func);
e = new_edge(bb_prev(bb), bb, EDGE_FLAG_FALLTHROUGH);
if (!e)
return true;
list_add_tail(&e->l, &bb->e_prevs);
bb = entry_bb(func);
bb = bb_next(bb);
list_for_each_entry_from(bb, &exit_bb(func)->l, l) {
e = new_edge(bb, NULL, EDGE_FLAG_EMPTY);
if (!e)
return true;
e->src = bb;
insn = bb->tail;
if (!is_jmp_insn(insn->code) ||
BPF_OP(insn->code) == BPF_CALL ||
BPF_OP(insn->code) == BPF_EXIT) {
e->dst = bb_next(bb);
e->flags |= EDGE_FLAG_FALLTHROUGH;
list_add_tail(&e->l, &bb->e_succs);
continue;
} else if (BPF_OP(insn->code) == BPF_JA) {
e->dst = func_search_bb_with_head(func,
insn + insn->off + 1);
e->flags |= EDGE_FLAG_JUMP;
list_add_tail(&e->l, &bb->e_succs);
continue;
}
e->dst = bb_next(bb);
e->flags |= EDGE_FLAG_FALLTHROUGH;
list_add_tail(&e->l, &bb->e_succs);
e = new_edge(bb, NULL, EDGE_FLAG_JUMP);
if (!e)
return true;
e->src = bb;
e->dst = func_search_bb_with_head(func, insn + insn->off + 1);
list_add_tail(&e->l, &bb->e_succs);
}
return false;
}
static bool cfg_build(struct cfg *cfg, struct bpf_insn *insn, unsigned int len)
{
int cnt = len / sizeof(*insn);
struct func_node *func;
INIT_LIST_HEAD(&cfg->funcs);
if (cfg_partition_funcs(cfg, insn, insn + cnt))
return true;
list_for_each_entry(func, &cfg->funcs, l) {
if (func_partition_bb(func) || func_add_special_bb(func))
return true;
if (func_add_bb_edges(func))
return true;
}
return false;
}
static void cfg_destroy(struct cfg *cfg)
{
struct func_node *func, *func2;
list_for_each_entry_safe(func, func2, &cfg->funcs, l) {
struct bb_node *bb, *bb2;
list_for_each_entry_safe(bb, bb2, &func->bbs, l) {
struct edge_node *e, *e2;
list_for_each_entry_safe(e, e2, &bb->e_prevs, l) {
list_del(&e->l);
free(e);
}
list_for_each_entry_safe(e, e2, &bb->e_succs, l) {
list_del(&e->l);
free(e);
}
list_del(&bb->l);
free(bb);
}
list_del(&func->l);
free(func);
}
}
static void
draw_bb_node(struct func_node *func, struct bb_node *bb, struct dump_data *dd,
bool opcodes, bool linum)
{
const char *shape;
if (bb->idx == ENTRY_BLOCK_INDEX || bb->idx == EXIT_BLOCK_INDEX)
shape = "Mdiamond";
else
shape = "record";
printf("\tfn_%d_bb_%d [shape=%s,style=filled,label=\"",
func->idx, bb->idx, shape);
if (bb->idx == ENTRY_BLOCK_INDEX) {
printf("ENTRY");
} else if (bb->idx == EXIT_BLOCK_INDEX) {
printf("EXIT");
} else {
unsigned int start_idx;
printf("{\\\n");
start_idx = bb->head - func->start;
dump_xlated_for_graph(dd, bb->head, bb->tail, start_idx,
opcodes, linum);
printf("}");
}
printf("\"];\n\n");
}
static void draw_bb_succ_edges(struct func_node *func, struct bb_node *bb)
{
const char *style = "\"solid,bold\"";
const char *color = "black";
int func_idx = func->idx;
struct edge_node *e;
int weight = 10;
if (list_empty(&bb->e_succs))
return;
list_for_each_entry(e, &bb->e_succs, l) {
printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=%s, color=%s, weight=%d, constraint=true",
func_idx, e->src->idx, func_idx, e->dst->idx,
style, color, weight);
printf("];\n");
}
}
static void
func_output_bb_def(struct func_node *func, struct dump_data *dd,
bool opcodes, bool linum)
{
struct bb_node *bb;
list_for_each_entry(bb, &func->bbs, l) {
draw_bb_node(func, bb, dd, opcodes, linum);
}
}
static void func_output_edges(struct func_node *func)
{
int func_idx = func->idx;
struct bb_node *bb;
list_for_each_entry(bb, &func->bbs, l) {
draw_bb_succ_edges(func, bb);
}
/* Add an invisible edge from ENTRY to EXIT, this is to
* improve the graph layout.
*/
printf("\tfn_%d_bb_%d:s -> fn_%d_bb_%d:n [style=\"invis\", constraint=true];\n",
func_idx, ENTRY_BLOCK_INDEX, func_idx, EXIT_BLOCK_INDEX);
}
static void
cfg_dump(struct cfg *cfg, struct dump_data *dd, bool opcodes, bool linum)
{
struct func_node *func;
printf("digraph \"DOT graph for eBPF program\" {\n");
list_for_each_entry(func, &cfg->funcs, l) {
printf("subgraph \"cluster_%d\" {\n\tstyle=\"dashed\";\n\tcolor=\"black\";\n\tlabel=\"func_%d ()\";\n",
func->idx, func->idx);
func_output_bb_def(func, dd, opcodes, linum);
func_output_edges(func);
printf("}\n");
}
printf("}\n");
}
void dump_xlated_cfg(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum)
{
struct bpf_insn *insn = buf;
struct cfg cfg;
memset(&cfg, 0, sizeof(cfg));
if (cfg_build(&cfg, insn, len))
return;
cfg_dump(&cfg, dd, opcodes, linum);
cfg_destroy(&cfg);
}
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (C) 2018 Netronome Systems, Inc. */
#ifndef __BPF_TOOL_CFG_H
#define __BPF_TOOL_CFG_H
#include "xlated_dumper.h"
void dump_xlated_cfg(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum);
#endif /* __BPF_TOOL_CFG_H */
+705
View File
@@ -0,0 +1,705 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (C) 2017 Facebook
// Author: Roman Gushchin <guro@fb.com>
#undef GCC_VERSION
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define _XOPEN_SOURCE 500
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include "main.h"
static const int cgroup_attach_types[] = {
BPF_CGROUP_INET_INGRESS,
BPF_CGROUP_INET_EGRESS,
BPF_CGROUP_INET_SOCK_CREATE,
BPF_CGROUP_INET_SOCK_RELEASE,
BPF_CGROUP_INET4_BIND,
BPF_CGROUP_INET6_BIND,
BPF_CGROUP_INET4_POST_BIND,
BPF_CGROUP_INET6_POST_BIND,
BPF_CGROUP_INET4_CONNECT,
BPF_CGROUP_INET6_CONNECT,
BPF_CGROUP_UNIX_CONNECT,
BPF_CGROUP_INET4_GETPEERNAME,
BPF_CGROUP_INET6_GETPEERNAME,
BPF_CGROUP_UNIX_GETPEERNAME,
BPF_CGROUP_INET4_GETSOCKNAME,
BPF_CGROUP_INET6_GETSOCKNAME,
BPF_CGROUP_UNIX_GETSOCKNAME,
BPF_CGROUP_UDP4_SENDMSG,
BPF_CGROUP_UDP6_SENDMSG,
BPF_CGROUP_UNIX_SENDMSG,
BPF_CGROUP_UDP4_RECVMSG,
BPF_CGROUP_UDP6_RECVMSG,
BPF_CGROUP_UNIX_RECVMSG,
BPF_CGROUP_SOCK_OPS,
BPF_CGROUP_DEVICE,
BPF_CGROUP_SYSCTL,
BPF_CGROUP_GETSOCKOPT,
BPF_CGROUP_SETSOCKOPT,
BPF_LSM_CGROUP
};
#define HELP_SPEC_ATTACH_FLAGS \
"ATTACH_FLAGS := { multi | override }"
#define HELP_SPEC_ATTACH_TYPES \
" ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
" cgroup_inet_sock_create | cgroup_sock_ops |\n" \
" cgroup_device | cgroup_inet4_bind |\n" \
" cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
" cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
" cgroup_inet6_connect | cgroup_unix_connect |\n" \
" cgroup_inet4_getpeername | cgroup_inet6_getpeername |\n" \
" cgroup_unix_getpeername | cgroup_inet4_getsockname |\n" \
" cgroup_inet6_getsockname | cgroup_unix_getsockname |\n" \
" cgroup_udp4_sendmsg | cgroup_udp6_sendmsg |\n" \
" cgroup_unix_sendmsg | cgroup_udp4_recvmsg |\n" \
" cgroup_udp6_recvmsg | cgroup_unix_recvmsg |\n" \
" cgroup_sysctl | cgroup_getsockopt |\n" \
" cgroup_setsockopt | cgroup_inet_sock_release }"
static unsigned int query_flags;
static struct btf *btf_vmlinux;
static __u32 btf_vmlinux_id;
static void free_btf_vmlinux(void)
{
btf__free(btf_vmlinux);
btf_vmlinux = NULL;
btf_vmlinux_id = 0;
}
static enum bpf_attach_type parse_attach_type(const char *str)
{
const char *attach_type_str;
enum bpf_attach_type type;
for (type = 0; ; type++) {
attach_type_str = libbpf_bpf_attach_type_str(type);
if (!attach_type_str)
break;
if (!strcmp(str, attach_type_str))
return type;
}
/* Also check traditionally used attach type strings. For these we keep
* allowing prefixed usage.
*/
for (type = 0; ; type++) {
attach_type_str = bpf_attach_type_input_str(type);
if (!attach_type_str)
break;
if (is_prefix(str, attach_type_str))
return type;
}
return __MAX_BPF_ATTACH_TYPE;
}
static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
{
struct bpf_btf_info btf_info = {};
__u32 btf_len = sizeof(btf_info);
char name[16] = {};
int err;
int fd;
btf_info.name = ptr_to_u64(name);
btf_info.name_len = sizeof(name);
fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
if (fd < 0)
return;
err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);
if (err)
goto out;
if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
btf_vmlinux_id = btf_info.id;
out:
close(fd);
}
static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
const char *attach_flags_str,
int level)
{
char prog_name[MAX_PROG_FULL_NAME];
const char *attach_btf_name = NULL;
struct bpf_prog_info info = {};
const char *attach_type_str;
__u32 info_len = sizeof(info);
int prog_fd;
prog_fd = bpf_prog_get_fd_by_id(id);
if (prog_fd < 0)
return -1;
if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {
close(prog_fd);
return -1;
}
attach_type_str = libbpf_bpf_attach_type_str(attach_type);
if (btf_vmlinux) {
if (!btf_vmlinux_id)
guess_vmlinux_btf_id(info.attach_btf_obj_id);
if (btf_vmlinux_id == info.attach_btf_obj_id &&
info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
const struct btf_type *t =
btf__type_by_id(btf_vmlinux, info.attach_btf_id);
attach_btf_name =
btf__name_by_offset(btf_vmlinux, t->name_off);
}
}
get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "id", info.id);
if (attach_type_str)
jsonw_string_field(json_wtr, "attach_type", attach_type_str);
else
jsonw_uint_field(json_wtr, "attach_type", attach_type);
if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
jsonw_string_field(json_wtr, "name", prog_name);
if (attach_btf_name)
jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
jsonw_end_object(json_wtr);
} else {
printf("%s%-8u ", level ? " " : "", info.id);
if (attach_type_str)
printf("%-15s", attach_type_str);
else
printf("type %-10u", attach_type);
if (query_flags & BPF_F_QUERY_EFFECTIVE)
printf(" %-15s", prog_name);
else
printf(" %-15s %-15s", attach_flags_str, prog_name);
if (attach_btf_name)
printf(" %-15s", attach_btf_name);
else if (info.attach_btf_id)
printf(" attach_btf_obj_id=%u attach_btf_id=%u",
info.attach_btf_obj_id, info.attach_btf_id);
printf("\n");
}
close(prog_fd);
return 0;
}
static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
{
__u32 prog_cnt = 0;
int ret;
ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
NULL, &prog_cnt);
if (ret)
return -1;
return prog_cnt;
}
static int cgroup_has_attached_progs(int cgroup_fd)
{
unsigned int i = 0;
bool no_prog = true;
for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) {
int count = count_attached_bpf_progs(cgroup_fd, cgroup_attach_types[i]);
if (count < 0 && errno != EINVAL)
return -1;
if (count > 0) {
no_prog = false;
break;
}
}
return no_prog ? 0 : 1;
}
static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
int level)
{
LIBBPF_OPTS(bpf_prog_query_opts, p);
__u32 prog_ids[1024] = {0};
__u32 iter;
int ret;
p.query_flags = query_flags;
p.prog_cnt = ARRAY_SIZE(prog_ids);
p.prog_ids = prog_ids;
ret = bpf_prog_query_opts(cgroup_fd, type, &p);
if (ret)
return ret;
if (p.prog_cnt == 0)
return 0;
for (iter = 0; iter < p.prog_cnt; iter++)
show_bpf_prog(prog_ids[iter], type, NULL, level);
return 0;
}
static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
int level)
{
LIBBPF_OPTS(bpf_prog_query_opts, p);
__u32 prog_attach_flags[1024] = {0};
const char *attach_flags_str;
__u32 prog_ids[1024] = {0};
char buf[32];
__u32 iter;
int ret;
p.query_flags = query_flags;
p.prog_cnt = ARRAY_SIZE(prog_ids);
p.prog_ids = prog_ids;
p.prog_attach_flags = prog_attach_flags;
ret = bpf_prog_query_opts(cgroup_fd, type, &p);
if (ret)
return ret;
if (p.prog_cnt == 0)
return 0;
for (iter = 0; iter < p.prog_cnt; iter++) {
__u32 attach_flags;
attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
switch (attach_flags) {
case BPF_F_ALLOW_MULTI:
attach_flags_str = "multi";
break;
case BPF_F_ALLOW_OVERRIDE:
attach_flags_str = "override";
break;
case 0:
attach_flags_str = "";
break;
default:
snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
attach_flags_str = buf;
}
show_bpf_prog(prog_ids[iter], type,
attach_flags_str, level);
}
return 0;
}
static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
int level)
{
return query_flags & BPF_F_QUERY_EFFECTIVE ?
show_effective_bpf_progs(cgroup_fd, type, level) :
show_attached_bpf_progs(cgroup_fd, type, level);
}
static int do_show(int argc, char **argv)
{
int has_attached_progs;
const char *path;
int cgroup_fd;
int ret = -1;
unsigned int i;
query_flags = 0;
if (!REQ_ARGS(1))
return -1;
path = GET_ARG();
while (argc) {
if (is_prefix(*argv, "effective")) {
if (query_flags & BPF_F_QUERY_EFFECTIVE) {
p_err("duplicated argument: %s", *argv);
return -1;
}
query_flags |= BPF_F_QUERY_EFFECTIVE;
NEXT_ARG();
} else {
p_err("expected no more arguments, 'effective', got: '%s'?",
*argv);
return -1;
}
}
cgroup_fd = open(path, O_RDONLY);
if (cgroup_fd < 0) {
p_err("can't open cgroup %s", path);
goto exit;
}
has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
if (has_attached_progs < 0) {
p_err("can't query bpf programs attached to %s: %s",
path, strerror(errno));
goto exit_cgroup;
} else if (!has_attached_progs) {
ret = 0;
goto exit_cgroup;
}
if (json_output)
jsonw_start_array(json_wtr);
else if (query_flags & BPF_F_QUERY_EFFECTIVE)
printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
else
printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
"AttachFlags", "Name");
btf_vmlinux = libbpf_find_kernel_btf();
for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) {
/*
* Not all attach types may be supported, so it's expected,
* that some requests will fail.
* If we were able to get the show for at least one
* attach type, let's return 0.
*/
if (show_bpf_progs(cgroup_fd, cgroup_attach_types[i], 0) == 0)
ret = 0;
}
if (json_output)
jsonw_end_array(json_wtr);
free_btf_vmlinux();
exit_cgroup:
close(cgroup_fd);
exit:
return ret;
}
/*
* To distinguish nftw() errors and do_show_tree_fn() errors
* and avoid duplicating error messages, let's return -2
* from do_show_tree_fn() in case of error.
*/
#define NFTW_ERR -1
#define SHOW_TREE_FN_ERR -2
static int do_show_tree_fn(const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftw)
{
int has_attached_progs;
int cgroup_fd;
unsigned int i;
if (typeflag != FTW_D)
return 0;
cgroup_fd = open(fpath, O_RDONLY);
if (cgroup_fd < 0) {
p_err("can't open cgroup %s: %s", fpath, strerror(errno));
return SHOW_TREE_FN_ERR;
}
has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
if (has_attached_progs < 0) {
p_err("can't query bpf programs attached to %s: %s",
fpath, strerror(errno));
close(cgroup_fd);
return SHOW_TREE_FN_ERR;
} else if (!has_attached_progs) {
close(cgroup_fd);
return 0;
}
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_string_field(json_wtr, "cgroup", fpath);
jsonw_name(json_wtr, "programs");
jsonw_start_array(json_wtr);
} else {
printf("%s\n", fpath);
}
if (!btf_vmlinux)
btf_vmlinux = libbpf_find_kernel_btf();
for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++)
show_bpf_progs(cgroup_fd, cgroup_attach_types[i], ftw->level);
if (errno == EINVAL)
/* Last attach type does not support query.
* Do not report an error for this, especially because batch
* mode would stop processing commands.
*/
errno = 0;
if (json_output) {
jsonw_end_array(json_wtr);
jsonw_end_object(json_wtr);
}
close(cgroup_fd);
return 0;
}
static char *find_cgroup_root(void)
{
struct mntent *mnt;
FILE *f;
f = fopen("/proc/mounts", "r");
if (f == NULL)
return NULL;
while ((mnt = getmntent(f))) {
if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
fclose(f);
return strdup(mnt->mnt_dir);
}
}
fclose(f);
return NULL;
}
static int do_show_tree(int argc, char **argv)
{
char *cgroup_root, *cgroup_alloced = NULL;
int ret;
query_flags = 0;
if (!argc) {
cgroup_alloced = find_cgroup_root();
if (!cgroup_alloced) {
p_err("cgroup v2 isn't mounted");
return -1;
}
cgroup_root = cgroup_alloced;
} else {
cgroup_root = GET_ARG();
while (argc) {
if (is_prefix(*argv, "effective")) {
if (query_flags & BPF_F_QUERY_EFFECTIVE) {
p_err("duplicated argument: %s", *argv);
return -1;
}
query_flags |= BPF_F_QUERY_EFFECTIVE;
NEXT_ARG();
} else {
p_err("expected no more arguments, 'effective', got: '%s'?",
*argv);
return -1;
}
}
}
if (json_output)
jsonw_start_array(json_wtr);
else if (query_flags & BPF_F_QUERY_EFFECTIVE)
printf("%s\n"
"%-8s %-15s %-15s\n",
"CgroupPath",
"ID", "AttachType", "Name");
else
printf("%s\n"
"%-8s %-15s %-15s %-15s\n",
"CgroupPath",
"ID", "AttachType", "AttachFlags", "Name");
switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
case NFTW_ERR:
p_err("can't iterate over %s: %s", cgroup_root,
strerror(errno));
ret = -1;
break;
case SHOW_TREE_FN_ERR:
ret = -1;
break;
default:
ret = 0;
}
if (json_output)
jsonw_end_array(json_wtr);
free_btf_vmlinux();
free(cgroup_alloced);
return ret;
}
static int do_attach(int argc, char **argv)
{
enum bpf_attach_type attach_type;
int cgroup_fd, prog_fd;
int attach_flags = 0;
int ret = -1;
int i;
if (argc < 4) {
p_err("too few parameters for cgroup attach");
goto exit;
}
cgroup_fd = open(argv[0], O_RDONLY);
if (cgroup_fd < 0) {
p_err("can't open cgroup %s", argv[0]);
goto exit;
}
attach_type = parse_attach_type(argv[1]);
if (attach_type == __MAX_BPF_ATTACH_TYPE) {
p_err("invalid attach type");
goto exit_cgroup;
}
argc -= 2;
argv = &argv[2];
prog_fd = prog_parse_fd(&argc, &argv);
if (prog_fd < 0)
goto exit_cgroup;
for (i = 0; i < argc; i++) {
if (is_prefix(argv[i], "multi")) {
attach_flags |= BPF_F_ALLOW_MULTI;
} else if (is_prefix(argv[i], "override")) {
attach_flags |= BPF_F_ALLOW_OVERRIDE;
} else {
p_err("unknown option: %s", argv[i]);
goto exit_cgroup;
}
}
if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
p_err("failed to attach program");
goto exit_prog;
}
if (json_output)
jsonw_null(json_wtr);
ret = 0;
exit_prog:
close(prog_fd);
exit_cgroup:
close(cgroup_fd);
exit:
return ret;
}
static int do_detach(int argc, char **argv)
{
enum bpf_attach_type attach_type;
int prog_fd, cgroup_fd;
int ret = -1;
if (argc < 4) {
p_err("too few parameters for cgroup detach");
goto exit;
}
cgroup_fd = open(argv[0], O_RDONLY);
if (cgroup_fd < 0) {
p_err("can't open cgroup %s", argv[0]);
goto exit;
}
attach_type = parse_attach_type(argv[1]);
if (attach_type == __MAX_BPF_ATTACH_TYPE) {
p_err("invalid attach type");
goto exit_cgroup;
}
argc -= 2;
argv = &argv[2];
prog_fd = prog_parse_fd(&argc, &argv);
if (prog_fd < 0)
goto exit_cgroup;
if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
p_err("failed to detach program");
goto exit_prog;
}
if (json_output)
jsonw_null(json_wtr);
ret = 0;
exit_prog:
close(prog_fd);
exit_cgroup:
close(cgroup_fd);
exit:
return ret;
}
static int do_help(int argc, char **argv)
{
if (json_output) {
jsonw_null(json_wtr);
return 0;
}
fprintf(stderr,
"Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
" %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
" %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
" %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
" %1$s %2$s help\n"
"\n"
HELP_SPEC_ATTACH_TYPES "\n"
" " HELP_SPEC_ATTACH_FLAGS "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS " |\n"
" {-f|--bpffs} }\n"
"",
bin_name, argv[-2]);
return 0;
}
static const struct cmd cmds[] = {
{ "show", do_show },
{ "list", do_show },
{ "tree", do_show_tree },
{ "attach", do_attach },
{ "detach", do_detach },
{ "help", do_help },
{ 0 }
};
int do_cgroup(int argc, char **argv)
{
return cmd_select(cmds, argc, argv, do_help);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+123
View File
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (C) 2020 Facebook
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <unistd.h>
#include <linux/err.h>
#include <bpf/libbpf.h>
#include "main.h"
static int do_pin(int argc, char **argv)
{
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, iter_opts);
union bpf_iter_link_info linfo;
const char *objfile, *path;
struct bpf_program *prog;
struct bpf_object *obj;
struct bpf_link *link;
int err = -1, map_fd = -1;
if (!REQ_ARGS(2))
usage();
objfile = GET_ARG();
path = GET_ARG();
/* optional arguments */
if (argc) {
if (is_prefix(*argv, "map")) {
NEXT_ARG();
if (!REQ_ARGS(2)) {
p_err("incorrect map spec");
return -1;
}
map_fd = map_parse_fd(&argc, &argv, BPF_F_RDONLY);
if (map_fd < 0)
return -1;
memset(&linfo, 0, sizeof(linfo));
linfo.map.map_fd = map_fd;
iter_opts.link_info = &linfo;
iter_opts.link_info_len = sizeof(linfo);
}
}
obj = bpf_object__open(objfile);
if (!obj) {
err = -errno;
p_err("can't open objfile %s", objfile);
goto close_map_fd;
}
err = bpf_object__load(obj);
if (err) {
p_err("can't load objfile %s", objfile);
goto close_obj;
}
prog = bpf_object__next_program(obj, NULL);
if (!prog) {
err = -errno;
p_err("can't find bpf program in objfile %s", objfile);
goto close_obj;
}
link = bpf_program__attach_iter(prog, &iter_opts);
if (!link) {
err = -errno;
p_err("attach_iter failed for program %s",
bpf_program__name(prog));
goto close_obj;
}
err = mount_bpffs_for_file(path);
if (err)
goto close_link;
err = bpf_link__pin(link, path);
if (err) {
p_err("pin_iter failed for program %s to path %s",
bpf_program__name(prog), path);
goto close_link;
}
close_link:
bpf_link__destroy(link);
close_obj:
bpf_object__close(obj);
close_map_fd:
if (map_fd >= 0)
close(map_fd);
return err;
}
static int do_help(int argc, char **argv)
{
fprintf(stderr,
"Usage: %1$s %2$s pin OBJ PATH [map MAP]\n"
" %1$s %2$s help\n"
"\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_OPTIONS " }\n"
"",
bin_name, "iter");
return 0;
}
static const struct cmd cmds[] = {
{ "help", do_help },
{ "pin", do_pin },
{ 0 }
};
int do_iter(int argc, char **argv)
{
return cmd_select(cmds, argc, argv, do_help);
}
+431
View File
@@ -0,0 +1,431 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/*
* Based on:
*
* Minimal BPF JIT image disassembler
*
* Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
* debugging or verification purposes.
*
* Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include <bpf/libbpf.h>
#ifdef HAVE_LLVM_SUPPORT
#include <llvm-c/Core.h>
#include <llvm-c/Disassembler.h>
#include <llvm-c/Target.h>
#include <llvm-c/TargetMachine.h>
#endif
#ifdef HAVE_LIBBFD_SUPPORT
#include <bfd.h>
#include <dis-asm.h>
#include <tools/dis-asm-compat.h>
#endif
#include "json_writer.h"
#include "main.h"
static int oper_count;
#ifdef HAVE_LLVM_SUPPORT
#define DISASM_SPACER
typedef LLVMDisasmContextRef disasm_ctx_t;
static int printf_json(char *s)
{
s = strtok(s, " \t");
jsonw_string_field(json_wtr, "operation", s);
jsonw_name(json_wtr, "operands");
jsonw_start_array(json_wtr);
oper_count = 1;
while ((s = strtok(NULL, " \t,()")) != 0) {
jsonw_string(json_wtr, s);
oper_count++;
}
return 0;
}
/* This callback to set the ref_type is necessary to have the LLVM disassembler
* print PC-relative addresses instead of byte offsets for branch instruction
* targets.
*/
static const char *
symbol_lookup_callback(__maybe_unused void *disasm_info,
__maybe_unused uint64_t ref_value,
uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
__maybe_unused const char **ref_name)
{
*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
return NULL;
}
static int
init_context(disasm_ctx_t *ctx, const char *arch,
__maybe_unused const char *disassembler_options,
__maybe_unused unsigned char *image, __maybe_unused ssize_t len,
__maybe_unused __u64 func_ksym)
{
char *triple;
if (arch)
triple = LLVMNormalizeTargetTriple(arch);
else
triple = LLVMGetDefaultTargetTriple();
if (!triple) {
p_err("Failed to retrieve triple");
return -1;
}
/*
* Enable all aarch64 ISA extensions so the disassembler can handle any
* instruction the kernel JIT might emit (e.g. ARM64 LSE atomics).
*/
if (!strncmp(triple, "aarch64", 7))
*ctx = LLVMCreateDisasmCPUFeatures(triple, "", "+all", NULL, 0, NULL,
symbol_lookup_callback);
else
*ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
LLVMDisposeMessage(triple);
if (!*ctx) {
p_err("Failed to create disassembler");
return -1;
}
return 0;
}
static void destroy_context(disasm_ctx_t *ctx)
{
LLVMDisposeMessage(*ctx);
}
static int
disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
__u64 func_ksym)
{
char buf[256];
int count;
count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
buf, sizeof(buf));
if (json_output)
printf_json(buf);
else
printf("%s", buf);
return count;
}
int disasm_init(void)
{
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();
return 0;
}
#endif /* HAVE_LLVM_SUPPORT */
#ifdef HAVE_LIBBFD_SUPPORT
#define DISASM_SPACER "\t"
struct disasm_info {
struct disassemble_info info;
__u64 func_ksym;
};
static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
{
struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
addr += dinfo->func_ksym;
generic_print_address(addr, info);
}
typedef struct {
struct disasm_info *info;
disassembler_ftype disassemble;
bfd *bfdf;
} disasm_ctx_t;
static int get_exec_path(char *tpath, size_t size)
{
const char *path = "/proc/self/exe";
ssize_t len;
len = readlink(path, tpath, size - 1);
if (len <= 0)
return -1;
tpath[len] = 0;
return 0;
}
static int printf_json(void *out, const char *fmt, va_list ap)
{
char *s;
int err;
err = vasprintf(&s, fmt, ap);
if (err < 0)
return -1;
if (!oper_count) {
int i;
/* Strip trailing spaces */
i = strlen(s) - 1;
while (s[i] == ' ')
s[i--] = '\0';
jsonw_string_field(json_wtr, "operation", s);
jsonw_name(json_wtr, "operands");
jsonw_start_array(json_wtr);
oper_count++;
} else if (!strcmp(fmt, ",")) {
/* Skip */
} else {
jsonw_string(json_wtr, s);
oper_count++;
}
free(s);
return 0;
}
static int fprintf_json(void *out, const char *fmt, ...)
{
va_list ap;
int r;
va_start(ap, fmt);
r = printf_json(out, fmt, ap);
va_end(ap);
return r;
}
static int fprintf_json_styled(void *out,
enum disassembler_style style __maybe_unused,
const char *fmt, ...)
{
va_list ap;
int r;
va_start(ap, fmt);
r = printf_json(out, fmt, ap);
va_end(ap);
return r;
}
static int init_context(disasm_ctx_t *ctx, const char *arch,
const char *disassembler_options,
unsigned char *image, ssize_t len, __u64 func_ksym)
{
struct disassemble_info *info;
char tpath[PATH_MAX];
bfd *bfdf;
memset(tpath, 0, sizeof(tpath));
if (get_exec_path(tpath, sizeof(tpath))) {
p_err("failed to create disassembler (get_exec_path)");
return -1;
}
ctx->bfdf = bfd_openr(tpath, NULL);
if (!ctx->bfdf) {
p_err("failed to create disassembler (bfd_openr)");
return -1;
}
if (!bfd_check_format(ctx->bfdf, bfd_object)) {
p_err("failed to create disassembler (bfd_check_format)");
goto err_close;
}
bfdf = ctx->bfdf;
ctx->info = malloc(sizeof(struct disasm_info));
if (!ctx->info) {
p_err("mem alloc failed");
goto err_close;
}
ctx->info->func_ksym = func_ksym;
info = &ctx->info->info;
if (json_output)
init_disassemble_info_compat(info, stdout,
(fprintf_ftype) fprintf_json,
fprintf_json_styled);
else
init_disassemble_info_compat(info, stdout,
(fprintf_ftype) fprintf,
fprintf_styled);
/* Update architecture info for offload. */
if (arch) {
const bfd_arch_info_type *inf = bfd_scan_arch(arch);
if (inf) {
bfdf->arch_info = inf;
} else {
p_err("No libbfd support for %s", arch);
goto err_free;
}
}
info->arch = bfd_get_arch(bfdf);
info->mach = bfd_get_mach(bfdf);
if (disassembler_options)
info->disassembler_options = disassembler_options;
info->buffer = image;
info->buffer_length = len;
info->print_address_func = disasm_print_addr;
disassemble_init_for_target(info);
#ifdef DISASM_FOUR_ARGS_SIGNATURE
ctx->disassemble = disassembler(info->arch,
bfd_big_endian(bfdf),
info->mach,
bfdf);
#else
ctx->disassemble = disassembler(bfdf);
#endif
if (!ctx->disassemble) {
p_err("failed to create disassembler");
goto err_free;
}
return 0;
err_free:
free(info);
err_close:
bfd_close(ctx->bfdf);
return -1;
}
static void destroy_context(disasm_ctx_t *ctx)
{
free(ctx->info);
bfd_close(ctx->bfdf);
}
static int
disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
__maybe_unused ssize_t len, int pc,
__maybe_unused __u64 func_ksym)
{
return ctx->disassemble(pc, &ctx->info->info);
}
int disasm_init(void)
{
bfd_init();
return 0;
}
#endif /* HAVE_LIBBPFD_SUPPORT */
int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
const char *arch, const char *disassembler_options,
const struct btf *btf,
const struct bpf_prog_linfo *prog_linfo,
__u64 func_ksym, unsigned int func_idx,
bool linum)
{
const struct bpf_line_info *linfo = NULL;
unsigned int nr_skip = 0;
int count, i;
unsigned int pc = 0;
disasm_ctx_t ctx;
if (!len)
return -1;
if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
return -1;
if (json_output)
jsonw_start_array(json_wtr);
do {
if (prog_linfo) {
linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
func_ksym + pc,
func_idx,
nr_skip);
if (linfo)
nr_skip++;
}
if (json_output) {
jsonw_start_object(json_wtr);
oper_count = 0;
if (linfo)
btf_dump_linfo_json(btf, linfo, linum);
jsonw_name(json_wtr, "pc");
jsonw_printf(json_wtr, "\"0x%x\"", pc);
} else {
if (linfo)
btf_dump_linfo_plain(btf, linfo, "; ",
linum);
printf("%4x:" DISASM_SPACER, pc);
}
count = disassemble_insn(&ctx, image, len, pc, func_ksym);
if (json_output) {
/* Operand array, was started in fprintf_json. Before
* that, make sure we have a _null_ value if no operand
* other than operation code was present.
*/
if (oper_count == 1)
jsonw_null(json_wtr);
jsonw_end_array(json_wtr);
}
if (opcodes) {
if (json_output) {
jsonw_name(json_wtr, "opcodes");
jsonw_start_array(json_wtr);
for (i = 0; i < count; ++i)
jsonw_printf(json_wtr, "\"0x%02hhx\"",
(uint8_t)image[pc + i]);
jsonw_end_array(json_wtr);
} else {
printf("\n\t");
for (i = 0; i < count; ++i)
printf("%02x ",
(uint8_t)image[pc + i]);
}
}
if (json_output)
jsonw_end_object(json_wtr);
else
printf("\n");
pc += count;
} while (count > 0 && pc < len);
if (json_output)
jsonw_end_array(json_wtr);
destroy_context(&ctx);
return 0;
}
+355
View File
@@ -0,0 +1,355 @@
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
/*
* Simple streaming JSON writer
*
* This takes care of the annoying bits of JSON syntax like the commas
* after elements
*
* Authors: Stephen Hemminger <stephen@networkplumber.org>
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdarg.h>
#include <assert.h>
#include <malloc.h>
#include <inttypes.h>
#include <stdint.h>
#include "json_writer.h"
struct json_writer {
FILE *out; /* output file */
unsigned depth; /* nesting */
bool pretty; /* optional whitepace */
char sep; /* either nul or comma */
};
/* indentation for pretty print */
static void jsonw_indent(json_writer_t *self)
{
unsigned i;
for (i = 0; i < self->depth; ++i)
fputs(" ", self->out);
}
/* end current line and indent if pretty printing */
static void jsonw_eol(json_writer_t *self)
{
if (!self->pretty)
return;
putc('\n', self->out);
jsonw_indent(self);
}
/* If current object is not empty print a comma */
static void jsonw_eor(json_writer_t *self)
{
if (self->sep != '\0')
putc(self->sep, self->out);
self->sep = ',';
}
/* Output JSON encoded string */
/* Handles C escapes, does not do Unicode */
static void jsonw_puts(json_writer_t *self, const char *str)
{
putc('"', self->out);
for (; *str; ++str)
switch (*str) {
case '\t':
fputs("\\t", self->out);
break;
case '\n':
fputs("\\n", self->out);
break;
case '\r':
fputs("\\r", self->out);
break;
case '\f':
fputs("\\f", self->out);
break;
case '\b':
fputs("\\b", self->out);
break;
case '\\':
fputs("\\\\", self->out);
break;
case '"':
fputs("\\\"", self->out);
break;
default:
putc(*str, self->out);
}
putc('"', self->out);
}
/* Create a new JSON stream */
json_writer_t *jsonw_new(FILE *f)
{
json_writer_t *self = malloc(sizeof(*self));
if (self) {
self->out = f;
self->depth = 0;
self->pretty = false;
self->sep = '\0';
}
return self;
}
/* End output to JSON stream */
void jsonw_destroy(json_writer_t **self_p)
{
json_writer_t *self = *self_p;
assert(self->depth == 0);
fputs("\n", self->out);
fflush(self->out);
free(self);
*self_p = NULL;
}
void jsonw_pretty(json_writer_t *self, bool on)
{
self->pretty = on;
}
void jsonw_reset(json_writer_t *self)
{
assert(self->depth == 0);
self->sep = '\0';
}
/* Basic blocks */
static void jsonw_begin(json_writer_t *self, int c)
{
jsonw_eor(self);
putc(c, self->out);
++self->depth;
self->sep = '\0';
}
static void jsonw_end(json_writer_t *self, int c)
{
assert(self->depth > 0);
--self->depth;
if (self->sep != '\0')
jsonw_eol(self);
putc(c, self->out);
self->sep = ',';
}
/* Add a JSON property name */
void jsonw_name(json_writer_t *self, const char *name)
{
jsonw_eor(self);
jsonw_eol(self);
self->sep = '\0';
jsonw_puts(self, name);
putc(':', self->out);
if (self->pretty)
putc(' ', self->out);
}
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
{
jsonw_eor(self);
putc('"', self->out);
vfprintf(self->out, fmt, ap);
putc('"', self->out);
}
void jsonw_printf(json_writer_t *self, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
jsonw_eor(self);
vfprintf(self->out, fmt, ap);
va_end(ap);
}
/* Collections */
void jsonw_start_object(json_writer_t *self)
{
jsonw_begin(self, '{');
}
void jsonw_end_object(json_writer_t *self)
{
jsonw_end(self, '}');
}
void jsonw_start_array(json_writer_t *self)
{
jsonw_begin(self, '[');
}
void jsonw_end_array(json_writer_t *self)
{
jsonw_end(self, ']');
}
/* JSON value types */
void jsonw_string(json_writer_t *self, const char *value)
{
jsonw_eor(self);
jsonw_puts(self, value);
}
void jsonw_bool(json_writer_t *self, bool val)
{
jsonw_printf(self, "%s", val ? "true" : "false");
}
void jsonw_null(json_writer_t *self)
{
jsonw_printf(self, "null");
}
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
{
jsonw_printf(self, fmt, num);
}
#ifdef notused
void jsonw_float(json_writer_t *self, double num)
{
jsonw_printf(self, "%g", num);
}
#endif
void jsonw_hu(json_writer_t *self, unsigned short num)
{
jsonw_printf(self, "%hu", num);
}
void jsonw_uint(json_writer_t *self, uint64_t num)
{
jsonw_printf(self, "%"PRIu64, num);
}
void jsonw_lluint(json_writer_t *self, unsigned long long int num)
{
jsonw_printf(self, "%llu", num);
}
void jsonw_int(json_writer_t *self, int64_t num)
{
jsonw_printf(self, "%"PRId64, num);
}
/* Basic name/value objects */
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
{
jsonw_name(self, prop);
jsonw_string(self, val);
}
void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
{
jsonw_name(self, prop);
jsonw_bool(self, val);
}
#ifdef notused
void jsonw_float_field(json_writer_t *self, const char *prop, double val)
{
jsonw_name(self, prop);
jsonw_float(self, val);
}
#endif
void jsonw_float_field_fmt(json_writer_t *self,
const char *prop,
const char *fmt,
double val)
{
jsonw_name(self, prop);
jsonw_float_fmt(self, fmt, val);
}
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
{
jsonw_name(self, prop);
jsonw_uint(self, num);
}
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
{
jsonw_name(self, prop);
jsonw_hu(self, num);
}
void jsonw_lluint_field(json_writer_t *self,
const char *prop,
unsigned long long int num)
{
jsonw_name(self, prop);
jsonw_lluint(self, num);
}
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
{
jsonw_name(self, prop);
jsonw_int(self, num);
}
void jsonw_null_field(json_writer_t *self, const char *prop)
{
jsonw_name(self, prop);
jsonw_null(self);
}
#ifdef TEST
int main(int argc, char **argv)
{
json_writer_t *wr = jsonw_new(stdout);
jsonw_start_object(wr);
jsonw_pretty(wr, true);
jsonw_name(wr, "Vyatta");
jsonw_start_object(wr);
jsonw_string_field(wr, "url", "http://vyatta.com");
jsonw_uint_field(wr, "downloads", 2000000ul);
jsonw_float_field(wr, "stock", 8.16);
jsonw_name(wr, "ARGV");
jsonw_start_array(wr);
while (--argc)
jsonw_string(wr, *++argv);
jsonw_end_array(wr);
jsonw_name(wr, "empty");
jsonw_start_array(wr);
jsonw_end_array(wr);
jsonw_name(wr, "NIL");
jsonw_start_object(wr);
jsonw_end_object(wr);
jsonw_null_field(wr, "my_null");
jsonw_name(wr, "special chars");
jsonw_start_array(wr);
jsonw_string_field(wr, "slash", "/");
jsonw_string_field(wr, "newline", "\n");
jsonw_string_field(wr, "tab", "\t");
jsonw_string_field(wr, "ff", "\f");
jsonw_string_field(wr, "quote", "\"");
jsonw_string_field(wr, "tick", "\'");
jsonw_string_field(wr, "backslash", "\\");
jsonw_end_array(wr);
jsonw_end_object(wr);
jsonw_end_object(wr);
jsonw_destroy(&wr);
return 0;
}
#endif
@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* Simple streaming JSON writer
*
* This takes care of the annoying bits of JSON syntax like the commas
* after elements
*
* Authors: Stephen Hemminger <stephen@networkplumber.org>
*/
#ifndef _JSON_WRITER_H_
#define _JSON_WRITER_H_
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#include <linux/compiler.h>
/* Opaque class structure */
typedef struct json_writer json_writer_t;
/* Create a new JSON stream */
json_writer_t *jsonw_new(FILE *f);
/* End output to JSON stream */
void jsonw_destroy(json_writer_t **self_p);
/* Cause output to have pretty whitespace */
void jsonw_pretty(json_writer_t *self, bool on);
/* Reset separator to create new JSON */
void jsonw_reset(json_writer_t *self);
/* Add property name */
void jsonw_name(json_writer_t *self, const char *name);
/* Add value */
void __printf(2, 0) jsonw_vprintf_enquote(json_writer_t *self, const char *fmt,
va_list ap);
void __printf(2, 3) jsonw_printf(json_writer_t *self, const char *fmt, ...);
void jsonw_string(json_writer_t *self, const char *value);
void jsonw_bool(json_writer_t *self, bool value);
void jsonw_float(json_writer_t *self, double number);
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
void jsonw_uint(json_writer_t *self, uint64_t number);
void jsonw_hu(json_writer_t *self, unsigned short number);
void jsonw_int(json_writer_t *self, int64_t number);
void jsonw_null(json_writer_t *self);
void jsonw_lluint(json_writer_t *self, unsigned long long int num);
/* Useful Combinations of name and value */
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
void jsonw_float_field(json_writer_t *self, const char *prop, double num);
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
void jsonw_null_field(json_writer_t *self, const char *prop);
void jsonw_lluint_field(json_writer_t *self, const char *prop,
unsigned long long int num);
void jsonw_float_field_fmt(json_writer_t *self, const char *prop,
const char *fmt, double val);
/* Collections */
void jsonw_start_object(json_writer_t *self);
void jsonw_end_object(json_writer_t *self);
void jsonw_start_array(json_writer_t *self);
void jsonw_end_array(json_writer_t *self);
/* Override default exception handling */
typedef void (jsonw_err_handler_fn)(const char *);
#endif /* _JSON_WRITER_H_ */
File diff suppressed because it is too large Load Diff
+579
View File
@@ -0,0 +1,579 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <linux/bpf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h>
#include "main.h"
#define BATCH_LINE_LEN_MAX 65536
#define BATCH_ARG_NB_MAX 4096
const char *bin_name;
static int last_argc;
static char **last_argv;
static int (*last_do_help)(int argc, char **argv);
json_writer_t *json_wtr;
bool pretty_output;
bool json_output;
bool show_pinned;
bool block_mount;
bool verifier_logs;
bool relaxed_maps;
bool use_loader;
struct btf *base_btf;
struct hashmap *refs_table;
bool sign_progs;
const char *private_key_path;
const char *cert_path;
static void __noreturn clean_and_exit(int i)
{
if (json_output)
jsonw_destroy(&json_wtr);
exit(i);
}
void usage(void)
{
last_do_help(last_argc - 1, last_argv + 1);
clean_and_exit(-1);
}
static int do_help(int argc, char **argv)
{
if (json_output) {
jsonw_null(json_wtr);
return 0;
}
fprintf(stderr,
"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
" %s batch file FILE\n"
" %s version\n"
"\n"
" OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter | token }\n"
" " HELP_SPEC_OPTIONS " |\n"
" {-V|--version} }\n"
"",
bin_name, bin_name, bin_name);
return 0;
}
static int do_batch(int argc, char **argv);
static int do_version(int argc, char **argv);
static const struct cmd commands[] = {
{ "help", do_help },
{ "batch", do_batch },
{ "prog", do_prog },
{ "map", do_map },
{ "link", do_link },
{ "cgroup", do_cgroup },
{ "perf", do_perf },
{ "net", do_net },
{ "feature", do_feature },
{ "btf", do_btf },
{ "gen", do_gen },
{ "struct_ops", do_struct_ops },
{ "iter", do_iter },
{ "token", do_token },
{ "version", do_version },
{ 0 }
};
#ifndef BPFTOOL_VERSION
/* bpftool's major and minor version numbers are aligned on libbpf's. There is
* an offset of 6 for the version number, because bpftool's version was higher
* than libbpf's when we adopted this scheme. The patch number remains at 0
* for now. Set BPFTOOL_VERSION to override.
*/
#define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
#define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
#define BPFTOOL_PATCH_VERSION 0
#endif
static void
print_feature(const char *feature, bool state, unsigned int *nb_features)
{
if (state) {
printf("%s %s", *nb_features ? "," : "", feature);
*nb_features = *nb_features + 1;
}
}
static int do_version(int argc, char **argv)
{
#ifdef HAVE_LIBBFD_SUPPORT
const bool has_libbfd = true;
#else
const bool has_libbfd = false;
#endif
#ifdef HAVE_LLVM_SUPPORT
const bool has_llvm = true;
#else
const bool has_llvm = false;
#endif
#ifdef BPFTOOL_WITHOUT_SKELETONS
const bool has_skeletons = false;
#else
const bool has_skeletons = true;
#endif
#ifdef BPFTOOL_WITHOUT_CRYPTO
const bool has_crypto = false;
#else
const bool has_crypto = true;
#endif
bool bootstrap = false;
int i;
for (i = 0; commands[i].cmd; i++) {
if (!strcmp(commands[i].cmd, "prog")) {
/* Assume we run a bootstrap version if "bpftool prog"
* is not available.
*/
bootstrap = !commands[i].func;
break;
}
}
if (json_output) {
jsonw_start_object(json_wtr); /* root object */
jsonw_name(json_wtr, "version");
#ifdef BPFTOOL_VERSION
jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
#else
jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
#endif
jsonw_name(json_wtr, "libbpf_version");
jsonw_printf(json_wtr, "\"%u.%u\"",
libbpf_major_version(), libbpf_minor_version());
jsonw_name(json_wtr, "features");
jsonw_start_object(json_wtr); /* features */
jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
jsonw_bool_field(json_wtr, "llvm", has_llvm);
jsonw_bool_field(json_wtr, "crypto", has_crypto);
jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
jsonw_bool_field(json_wtr, "bootstrap", bootstrap);
jsonw_end_object(json_wtr); /* features */
jsonw_end_object(json_wtr); /* root object */
} else {
unsigned int nb_features = 0;
#ifdef BPFTOOL_VERSION
printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
#else
printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
#endif
printf("using libbpf %s\n", libbpf_version_string());
printf("features:");
print_feature("libbfd", has_libbfd, &nb_features);
print_feature("llvm", has_llvm, &nb_features);
print_feature("crypto", has_crypto, &nb_features);
print_feature("skeletons", has_skeletons, &nb_features);
print_feature("bootstrap", bootstrap, &nb_features);
printf("\n");
}
return 0;
}
int cmd_select(const struct cmd *cmds, int argc, char **argv,
int (*help)(int argc, char **argv))
{
unsigned int i;
last_argc = argc;
last_argv = argv;
last_do_help = help;
if (argc < 1 && cmds[0].func)
return cmds[0].func(argc, argv);
for (i = 0; cmds[i].cmd; i++) {
if (is_prefix(*argv, cmds[i].cmd)) {
if (!cmds[i].func) {
p_err("command '%s' is not supported in bootstrap mode",
cmds[i].cmd);
return -1;
}
return cmds[i].func(argc - 1, argv + 1);
}
}
help(argc - 1, argv + 1);
return -1;
}
bool is_prefix(const char *pfx, const char *str)
{
if (!pfx)
return false;
if (strlen(str) < strlen(pfx))
return false;
return !memcmp(str, pfx, strlen(pfx));
}
/* Last argument MUST be NULL pointer */
int detect_common_prefix(const char *arg, ...)
{
unsigned int count = 0;
const char *ref;
char msg[256];
va_list ap;
snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
va_start(ap, arg);
while ((ref = va_arg(ap, const char *))) {
if (!is_prefix(arg, ref))
continue;
count++;
if (count > 1)
strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
}
va_end(ap);
strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
if (count >= 2) {
p_err("%s", msg);
return -1;
}
return 0;
}
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
{
unsigned char *data = arg;
unsigned int i;
for (i = 0; i < n; i++) {
const char *pfx = "";
if (!i)
/* nothing */;
else if (!(i % 16))
fprintf(f, "\n");
else if (!(i % 8))
fprintf(f, " ");
else
pfx = sep;
fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
}
}
/* Split command line into argument vector. */
static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
{
static const char ws[] = " \t\r\n";
char *cp = line;
int n_argc = 0;
while (*cp) {
/* Skip leading whitespace. */
cp += strspn(cp, ws);
if (*cp == '\0')
break;
if (n_argc >= (maxargs - 1)) {
p_err("too many arguments to command %d", cmd_nb);
return -1;
}
/* Word begins with quote. */
if (*cp == '\'' || *cp == '"') {
char quote = *cp++;
n_argv[n_argc++] = cp;
/* Find ending quote. */
cp = strchr(cp, quote);
if (!cp) {
p_err("unterminated quoted string in command %d",
cmd_nb);
return -1;
}
} else {
n_argv[n_argc++] = cp;
/* Find end of word. */
cp += strcspn(cp, ws);
if (*cp == '\0')
break;
}
/* Separate words. */
*cp++ = 0;
}
n_argv[n_argc] = NULL;
return n_argc;
}
static int do_batch(int argc, char **argv)
{
char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
char *n_argv[BATCH_ARG_NB_MAX];
unsigned int lines = 0;
int n_argc;
FILE *fp;
char *cp;
int err = 0;
int i;
if (argc < 2) {
p_err("too few parameters for batch");
return -1;
} else if (argc > 2) {
p_err("too many parameters for batch");
return -1;
} else if (!is_prefix(*argv, "file")) {
p_err("expected 'file', got: %s", *argv);
return -1;
}
NEXT_ARG();
if (!strcmp(*argv, "-"))
fp = stdin;
else
fp = fopen(*argv, "r");
if (!fp) {
p_err("Can't open file (%s): %s", *argv, strerror(errno));
return -1;
}
if (json_output)
jsonw_start_array(json_wtr);
while (fgets(buf, sizeof(buf), fp)) {
cp = strchr(buf, '#');
if (cp)
*cp = '\0';
if (strlen(buf) == sizeof(buf) - 1) {
errno = E2BIG;
break;
}
/* Append continuation lines if any (coming after a line ending
* with '\' in the batch file).
*/
while ((cp = strstr(buf, "\\\n")) != NULL) {
if (!fgets(contline, sizeof(contline), fp) ||
strlen(contline) == 0) {
p_err("missing continuation line on command %u",
lines);
err = -1;
goto err_close;
}
cp = strchr(contline, '#');
if (cp)
*cp = '\0';
if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
p_err("command %u is too long", lines);
err = -1;
goto err_close;
}
buf[strlen(buf) - 2] = '\0';
strcat(buf, contline);
}
n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
if (!n_argc)
continue;
if (n_argc < 0) {
err = n_argc;
goto err_close;
}
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "command");
jsonw_start_array(json_wtr);
for (i = 0; i < n_argc; i++)
jsonw_string(json_wtr, n_argv[i]);
jsonw_end_array(json_wtr);
jsonw_name(json_wtr, "output");
}
err = cmd_select(commands, n_argc, n_argv, do_help);
if (json_output)
jsonw_end_object(json_wtr);
if (err)
goto err_close;
lines++;
}
if (errno && errno != ENOENT) {
p_err("reading batch file failed: %s", strerror(errno));
err = -1;
} else {
if (!json_output)
printf("processed %u commands\n", lines);
}
err_close:
if (fp != stdin)
fclose(fp);
if (json_output)
jsonw_end_array(json_wtr);
return err;
}
int main(int argc, char **argv)
{
static const struct option options[] = {
{ "json", no_argument, NULL, 'j' },
{ "help", no_argument, NULL, 'h' },
{ "pretty", no_argument, NULL, 'p' },
{ "version", no_argument, NULL, 'V' },
{ "bpffs", no_argument, NULL, 'f' },
{ "mapcompat", no_argument, NULL, 'm' },
{ "nomount", no_argument, NULL, 'n' },
{ "debug", no_argument, NULL, 'd' },
{ "use-loader", no_argument, NULL, 'L' },
{ "sign", no_argument, NULL, 'S' },
{ "base-btf", required_argument, NULL, 'B' },
{ 0 }
};
bool version_requested = false;
int opt, ret;
setlinebuf(stdout);
#ifdef USE_LIBCAP
/* Libcap < 2.63 hooks before main() to compute the number of
* capabilities of the running kernel, and doing so it calls prctl()
* which may fail and set errno to non-zero.
* Let's reset errno to make sure this does not interfere with the
* batch mode.
*/
errno = 0;
#endif
last_do_help = do_help;
pretty_output = false;
json_output = false;
show_pinned = false;
block_mount = false;
bin_name = "bpftool";
opterr = 0;
while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
options, NULL)) >= 0) {
switch (opt) {
case 'V':
version_requested = true;
break;
case 'h':
return do_help(argc, argv);
case 'p':
pretty_output = true;
/* fall through */
case 'j':
if (!json_output) {
json_wtr = jsonw_new(stdout);
if (!json_wtr) {
p_err("failed to create JSON writer");
return -1;
}
json_output = true;
}
jsonw_pretty(json_wtr, pretty_output);
break;
case 'f':
show_pinned = true;
break;
case 'm':
relaxed_maps = true;
break;
case 'n':
block_mount = true;
break;
case 'd':
libbpf_set_print(print_all_levels);
verifier_logs = true;
break;
case 'B':
base_btf = btf__parse(optarg, NULL);
if (!base_btf) {
p_err("failed to parse base BTF at '%s': %d\n",
optarg, -errno);
return -1;
}
break;
case 'L':
use_loader = true;
break;
case 'S':
sign_progs = true;
use_loader = true;
break;
case 'k':
private_key_path = optarg;
break;
case 'i':
cert_path = optarg;
break;
default:
p_err("unrecognized option '%s'", argv[optind - 1]);
if (json_output)
clean_and_exit(-1);
else
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 0)
usage();
if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
p_err("-i <identity_x509_cert> and -k <private_key> must be supplied with -S for signing");
return -EINVAL;
}
if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
p_err("--sign (or -S) must be explicitly passed with -i <identity_x509_cert> and -k <private_key> to sign the programs");
return -EINVAL;
}
if (version_requested)
ret = do_version(argc, argv);
else
ret = cmd_select(commands, argc, argv, do_help);
if (json_output)
jsonw_destroy(&json_wtr);
btf__free(base_btf);
return ret;
}
+312
View File
@@ -0,0 +1,312 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
#ifndef __BPF_TOOL_H
#define __BPF_TOOL_H
/* BFD and kernel.h both define GCC_VERSION, differently */
#undef GCC_VERSION
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <bpf/skel_internal.h>
#include <linux/bpf.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <bpf/hashmap.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "json_writer.h"
/* Make sure we do not use kernel-only integer typedefs */
#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64)(unsigned long)ptr;
}
static inline void *u64_to_ptr(__u64 ptr)
{
return (void *)(unsigned long)ptr;
}
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
#define BAD_ARG() ({ p_err("what is '%s'?", *argv); -1; })
#define GET_ARG() ({ argc--; *argv++; })
#define REQ_ARGS(cnt) \
({ \
int _cnt = (cnt); \
bool _res; \
\
if (argc < _cnt) { \
p_err("'%s' needs at least %d arguments, %d found", \
argv[-1], _cnt, argc); \
_res = false; \
} else { \
_res = true; \
} \
_res; \
})
#define ERR_MAX_LEN 1024
#define MAX_SIG_SIZE 4096
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
#define HELP_SPEC_PROGRAM \
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG | name PROG_NAME }"
#define HELP_SPEC_OPTIONS \
"OPTIONS := { {-j|--json} [{-p|--pretty}] | {-d|--debug}"
#define HELP_SPEC_MAP \
"MAP := { id MAP_ID | pinned FILE | name MAP_NAME }"
#define HELP_SPEC_LINK \
"LINK := { id LINK_ID | pinned FILE }"
/* keep in sync with the definition in skeleton/pid_iter.bpf.c */
enum bpf_obj_type {
BPF_OBJ_UNKNOWN,
BPF_OBJ_PROG,
BPF_OBJ_MAP,
BPF_OBJ_LINK,
BPF_OBJ_BTF,
};
extern const char *bin_name;
extern json_writer_t *json_wtr;
extern bool json_output;
extern bool show_pinned;
extern bool show_pids;
extern bool block_mount;
extern bool verifier_logs;
extern bool relaxed_maps;
extern bool use_loader;
extern struct btf *base_btf;
extern struct hashmap *refs_table;
extern bool sign_progs;
extern const char *private_key_path;
extern const char *cert_path;
void __printf(1, 2) p_err(const char *fmt, ...);
void __printf(1, 2) p_info(const char *fmt, ...);
bool is_prefix(const char *pfx, const char *str);
int detect_common_prefix(const char *arg, ...);
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
void usage(void) __noreturn;
void set_max_rlimit(void);
int mount_tracefs(const char *target);
struct obj_ref {
int pid;
char comm[16];
};
struct obj_refs {
int ref_cnt;
bool has_bpf_cookie;
struct obj_ref *refs;
__u64 bpf_cookie;
};
struct btf;
struct bpf_line_info;
int build_pinned_obj_table(struct hashmap *table,
enum bpf_obj_type type);
void delete_pinned_obj_table(struct hashmap *table);
__weak int build_obj_refs_table(struct hashmap **table,
enum bpf_obj_type type);
__weak void delete_obj_refs_table(struct hashmap *table);
__weak void emit_obj_refs_json(struct hashmap *table, __u32 id,
json_writer_t *json_wtr);
__weak void emit_obj_refs_plain(struct hashmap *table, __u32 id,
const char *prefix);
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
struct cmd {
const char *cmd;
int (*func)(int argc, char **argv);
};
int cmd_select(const struct cmd *cmds, int argc, char **argv,
int (*help)(int argc, char **argv));
#define MAX_PROG_FULL_NAME 128
void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd,
char *name_buff, size_t buff_len);
int get_fd_type(int fd);
const char *get_fd_type_name(enum bpf_obj_type type);
char *get_fdinfo(int fd, const char *key);
int open_obj_pinned(const char *path, bool quiet,
const struct bpf_obj_get_opts *opts);
int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type,
const struct bpf_obj_get_opts *opts);
int mount_bpffs_for_file(const char *file_name);
int create_and_mount_bpffs_dir(const char *dir_name);
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(int *, char ***));
int do_pin_fd(int fd, const char *name);
/* commands available in bootstrap mode */
int do_gen(int argc, char **argv);
int do_btf(int argc, char **argv);
/* non-bootstrap only commands */
int do_prog(int argc, char **arg) __weak;
int do_map(int argc, char **arg) __weak;
int do_link(int argc, char **arg) __weak;
int do_event_pipe(int argc, char **argv) __weak;
int do_cgroup(int argc, char **arg) __weak;
int do_perf(int argc, char **arg) __weak;
int do_net(int argc, char **arg) __weak;
int do_tracelog(int argc, char **arg) __weak;
int do_feature(int argc, char **argv) __weak;
int do_struct_ops(int argc, char **argv) __weak;
int do_iter(int argc, char **argv) __weak;
int do_token(int argc, char **argv) __weak;
int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
int prog_parse_fd(int *argc, char ***argv);
int prog_parse_fds(int *argc, char ***argv, int **fds);
int map_parse_fd(int *argc, char ***argv, __u32 open_flags);
int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags);
int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info,
__u32 *info_len, __u32 open_flags);
struct bpf_prog_linfo;
#if defined(HAVE_LLVM_SUPPORT) || defined(HAVE_LIBBFD_SUPPORT)
int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
const char *arch, const char *disassembler_options,
const struct btf *btf,
const struct bpf_prog_linfo *prog_linfo,
__u64 func_ksym, unsigned int func_idx,
bool linum);
int disasm_init(void);
#else
static inline
int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
const char *arch, const char *disassembler_options,
const struct btf *btf,
const struct bpf_prog_linfo *prog_linfo,
__u64 func_ksym, unsigned int func_idx,
bool linum)
{
return 0;
}
static inline int disasm_init(void)
{
p_err("No JIT disassembly support");
return -1;
}
#endif
void print_data_json(uint8_t *data, size_t len);
void print_hex_data_json(uint8_t *data, size_t len);
unsigned int get_page_size(void);
unsigned int get_possible_cpus(void);
const char *
ifindex_to_arch(__u32 ifindex, __u64 ns_dev, __u64 ns_ino, const char **opt);
struct btf_dumper {
const struct btf *btf;
json_writer_t *jw;
bool is_plain_text;
bool prog_id_as_func_ptr;
};
/* btf_dumper_type - print data along with type information
* @d: an instance containing context for dumping types
* @type_id: index in btf->types array. this points to the type to be dumped
* @data: pointer the actual data, i.e. the values to be printed
*
* Returns zero on success and negative error code otherwise
*/
int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
const void *data);
void btf_dumper_type_only(const struct btf *btf, __u32 func_type_id,
char *func_only, int size);
void btf_dump_linfo_plain(const struct btf *btf,
const struct bpf_line_info *linfo,
const char *prefix, bool linum);
void btf_dump_linfo_json(const struct btf *btf,
const struct bpf_line_info *linfo, bool linum);
void btf_dump_linfo_dotlabel(const struct btf *btf,
const struct bpf_line_info *linfo, bool linum);
struct nlattr;
struct ifinfomsg;
struct tcmsg;
int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb);
int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind,
const char *devname, int ifindex);
int print_all_levels(__maybe_unused enum libbpf_print_level level,
const char *format, va_list args);
size_t hash_fn_for_key_as_id(long key, void *ctx);
bool equal_fn_for_key_as_id(long k1, long k2, void *ctx);
/* bpf_attach_type_input_str - convert the provided attach type value into a
* textual representation that we accept for input purposes.
*
* This function is similar in nature to libbpf_bpf_attach_type_str, but
* recognizes some attach type names that have been used by the program in the
* past and which do not follow the string inference scheme that libbpf uses.
* These textual representations should only be used for user input.
*
* @t: The attach type
* Returns a pointer to a static string identifying the attach type. NULL is
* returned for unknown bpf_attach_type values.
*/
const char *bpf_attach_type_input_str(enum bpf_attach_type t);
static inline bool hashmap__empty(struct hashmap *map)
{
return map ? hashmap__size(map) == 0 : true;
}
int pathname_concat(char *buf, int buf_sz, const char *path,
const char *name);
/* print netfilter bpf_link info */
void netfilter_dump_plain(const struct bpf_link_info *info);
void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr);
struct kernel_config_option {
const char *name;
bool macro_dump;
};
int read_kernel_config(const struct kernel_config_option *requested_options,
size_t num_options, char **out_values,
const char *define_prefix);
#ifndef BPFTOOL_WITHOUT_CRYPTO
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts);
__u32 register_session_key(const char *key_der_path);
#else
static inline int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
{
p_err("bpftool was built without signing support");
return -ENOTSUP;
}
static inline __u32 register_session_key(const char *key_der_path)
{
p_err("bpftool was built without signing support");
return -1;
}
#endif
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2018 Netronome Systems, Inc. */
/* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include <errno.h>
#include <fcntl.h>
#include <bpf/libbpf.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/bpf.h>
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <bpf/bpf.h>
#include "main.h"
#define MMAP_PAGE_CNT 16
static volatile bool stop;
struct perf_event_sample {
struct perf_event_header header;
__u64 time;
__u32 size;
unsigned char data[];
};
struct perf_event_lost {
struct perf_event_header header;
__u64 id;
__u64 lost;
};
static void int_exit(int signo)
{
fprintf(stderr, "Stopping...\n");
stop = true;
}
struct event_pipe_ctx {
bool all_cpus;
int cpu;
int idx;
};
static enum bpf_perf_event_ret
print_bpf_output(void *private_data, int cpu, struct perf_event_header *event)
{
struct perf_event_sample *e = container_of(event,
struct perf_event_sample,
header);
struct perf_event_lost *lost = container_of(event,
struct perf_event_lost,
header);
struct event_pipe_ctx *ctx = private_data;
int idx = ctx->all_cpus ? cpu : ctx->idx;
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "type");
jsonw_uint(json_wtr, e->header.type);
jsonw_name(json_wtr, "cpu");
jsonw_uint(json_wtr, cpu);
jsonw_name(json_wtr, "index");
jsonw_uint(json_wtr, idx);
if (e->header.type == PERF_RECORD_SAMPLE) {
jsonw_name(json_wtr, "timestamp");
jsonw_uint(json_wtr, e->time);
jsonw_name(json_wtr, "data");
print_data_json(e->data, e->size);
} else if (e->header.type == PERF_RECORD_LOST) {
jsonw_name(json_wtr, "lost");
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "id");
jsonw_uint(json_wtr, lost->id);
jsonw_name(json_wtr, "count");
jsonw_uint(json_wtr, lost->lost);
jsonw_end_object(json_wtr);
}
jsonw_end_object(json_wtr);
} else {
if (e->header.type == PERF_RECORD_SAMPLE) {
printf("== @%llu.%09llu CPU: %d index: %d =====\n",
e->time / 1000000000ULL, e->time % 1000000000ULL,
cpu, idx);
fprint_hex(stdout, e->data, e->size, " ");
printf("\n");
} else if (e->header.type == PERF_RECORD_LOST) {
printf("lost %llu events\n", lost->lost);
} else {
printf("unknown event type=%u size=%u\n",
e->header.type, e->header.size);
}
}
return LIBBPF_PERF_EVENT_CONT;
}
int do_event_pipe(int argc, char **argv)
{
struct perf_event_attr perf_attr = {
.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME,
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_BPF_OUTPUT,
.sample_period = 1,
.wakeup_events = 1,
};
struct bpf_map_info map_info = {};
LIBBPF_OPTS(perf_buffer_raw_opts, opts);
struct event_pipe_ctx ctx = {
.all_cpus = true,
.cpu = -1,
.idx = -1,
};
struct perf_buffer *pb;
__u32 map_info_len;
int err, map_fd;
map_info_len = sizeof(map_info);
map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len,
0);
if (map_fd < 0)
return -1;
if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
p_err("map is not a perf event array");
goto err_close_map;
}
while (argc) {
if (argc < 2) {
BAD_ARG();
goto err_close_map;
}
if (is_prefix(*argv, "cpu")) {
char *endptr;
NEXT_ARG();
ctx.cpu = strtoul(*argv, &endptr, 0);
if (*endptr) {
p_err("can't parse %s as CPU ID", *argv);
goto err_close_map;
}
NEXT_ARG();
} else if (is_prefix(*argv, "index")) {
char *endptr;
NEXT_ARG();
ctx.idx = strtoul(*argv, &endptr, 0);
if (*endptr) {
p_err("can't parse %s as index", *argv);
goto err_close_map;
}
NEXT_ARG();
} else {
BAD_ARG();
goto err_close_map;
}
ctx.all_cpus = false;
}
if (!ctx.all_cpus) {
if (ctx.idx == -1 || ctx.cpu == -1) {
p_err("cpu and index must be specified together");
goto err_close_map;
}
} else {
ctx.cpu = 0;
ctx.idx = 0;
}
opts.cpu_cnt = ctx.all_cpus ? 0 : 1;
opts.cpus = &ctx.cpu;
opts.map_keys = &ctx.idx;
pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &perf_attr,
print_bpf_output, &ctx, &opts);
if (!pb) {
p_err("failed to create perf buffer: %s (%d)",
strerror(errno), errno);
goto err_close_map;
}
signal(SIGINT, int_exit);
signal(SIGHUP, int_exit);
signal(SIGTERM, int_exit);
if (json_output)
jsonw_start_array(json_wtr);
while (!stop) {
err = perf_buffer__poll(pb, 200);
if (err < 0 && err != -EINTR) {
p_err("perf buffer polling failed: %s (%d)",
strerror(errno), errno);
goto err_close_pb;
}
}
if (json_output)
jsonw_end_array(json_wtr);
perf_buffer__free(pb);
close(map_fd);
return 0;
err_close_pb:
perf_buffer__free(pb);
err_close_map:
close(map_fd);
return -1;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,178 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (C) 2018 Facebook
#include <stdlib.h>
#include <string.h>
#include <bpf/libbpf.h>
#include <linux/rtnetlink.h>
#include <linux/tc_act/tc_bpf.h>
#include "bpf/nlattr.h"
#include "main.h"
#include "netlink_dumper.h"
static void xdp_dump_prog_id(struct nlattr **tb, int attr,
const char *mode,
bool new_json_object)
{
if (!tb[attr])
return;
if (new_json_object)
NET_START_OBJECT
NET_DUMP_STR("mode", " %s", mode);
NET_DUMP_UINT("id", " id %u", libbpf_nla_getattr_u32(tb[attr]))
if (new_json_object)
NET_END_OBJECT
}
static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex,
const char *name)
{
struct nlattr *tb[IFLA_XDP_MAX + 1];
unsigned char mode;
if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0)
return -1;
if (!tb[IFLA_XDP_ATTACHED])
return 0;
mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]);
if (mode == XDP_ATTACHED_NONE)
return 0;
NET_START_OBJECT;
if (name)
NET_DUMP_STR("devname", "%s", name);
NET_DUMP_UINT("ifindex", "(%u)", ifindex);
if (mode == XDP_ATTACHED_MULTI) {
if (json_output) {
jsonw_name(json_wtr, "multi_attachments");
jsonw_start_array(json_wtr);
}
xdp_dump_prog_id(tb, IFLA_XDP_SKB_PROG_ID, "generic", true);
xdp_dump_prog_id(tb, IFLA_XDP_DRV_PROG_ID, "driver", true);
xdp_dump_prog_id(tb, IFLA_XDP_HW_PROG_ID, "offload", true);
if (json_output)
jsonw_end_array(json_wtr);
} else if (mode == XDP_ATTACHED_DRV) {
xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "driver", false);
} else if (mode == XDP_ATTACHED_SKB) {
xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "generic", false);
} else if (mode == XDP_ATTACHED_HW) {
xdp_dump_prog_id(tb, IFLA_XDP_PROG_ID, "offload", false);
}
NET_END_OBJECT_FINAL;
return 0;
}
int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb)
{
if (!tb[IFLA_XDP])
return 0;
return do_xdp_dump_one(tb[IFLA_XDP], (unsigned int)ifinfo->ifi_index,
libbpf_nla_getattr_str(tb[IFLA_IFNAME]));
}
static int do_bpf_dump_one_act(struct nlattr *attr)
{
struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
if (libbpf_nla_parse_nested(tb, TCA_ACT_BPF_MAX, attr, NULL) < 0)
return -LIBBPF_ERRNO__NLPARSE;
if (!tb[TCA_ACT_BPF_PARMS])
return -LIBBPF_ERRNO__NLPARSE;
NET_START_OBJECT_NESTED2;
if (tb[TCA_ACT_BPF_NAME])
NET_DUMP_STR("name", "%s",
libbpf_nla_getattr_str(tb[TCA_ACT_BPF_NAME]));
if (tb[TCA_ACT_BPF_ID])
NET_DUMP_UINT("id", " id %u",
libbpf_nla_getattr_u32(tb[TCA_ACT_BPF_ID]));
NET_END_OBJECT_NESTED;
return 0;
}
static int do_dump_one_act(struct nlattr *attr)
{
struct nlattr *tb[TCA_ACT_MAX + 1];
if (!attr)
return 0;
if (libbpf_nla_parse_nested(tb, TCA_ACT_MAX, attr, NULL) < 0)
return -LIBBPF_ERRNO__NLPARSE;
if (tb[TCA_ACT_KIND] &&
strcmp(libbpf_nla_data(tb[TCA_ACT_KIND]), "bpf") == 0)
return do_bpf_dump_one_act(tb[TCA_ACT_OPTIONS]);
return 0;
}
static int do_bpf_act_dump(struct nlattr *attr)
{
struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
int act, ret;
if (libbpf_nla_parse_nested(tb, TCA_ACT_MAX_PRIO, attr, NULL) < 0)
return -LIBBPF_ERRNO__NLPARSE;
NET_START_ARRAY("act", " %s [");
for (act = 0; act <= TCA_ACT_MAX_PRIO; act++) {
ret = do_dump_one_act(tb[act]);
if (ret)
break;
}
NET_END_ARRAY("] ");
return ret;
}
static int do_bpf_filter_dump(struct nlattr *attr)
{
struct nlattr *tb[TCA_BPF_MAX + 1];
int ret;
if (libbpf_nla_parse_nested(tb, TCA_BPF_MAX, attr, NULL) < 0)
return -LIBBPF_ERRNO__NLPARSE;
if (tb[TCA_BPF_NAME])
NET_DUMP_STR("name", " %s",
libbpf_nla_getattr_str(tb[TCA_BPF_NAME]));
if (tb[TCA_BPF_ID])
NET_DUMP_UINT("id", " id %u",
libbpf_nla_getattr_u32(tb[TCA_BPF_ID]));
if (tb[TCA_BPF_ACT]) {
ret = do_bpf_act_dump(tb[TCA_BPF_ACT]);
if (ret)
return ret;
}
return 0;
}
int do_filter_dump(struct tcmsg *info, struct nlattr **tb, const char *kind,
const char *devname, int ifindex)
{
int ret = 0;
if (tb[TCA_OPTIONS] &&
strcmp(libbpf_nla_data(tb[TCA_KIND]), "bpf") == 0) {
NET_START_OBJECT;
if (devname[0] != '\0')
NET_DUMP_STR("devname", "%s", devname);
NET_DUMP_UINT("ifindex", "(%u)", (unsigned int)ifindex);
NET_DUMP_STR("kind", " %s", kind);
ret = do_bpf_filter_dump(tb[TCA_OPTIONS]);
NET_END_OBJECT_FINAL;
}
return ret;
}
@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
// Copyright (C) 2018 Facebook
#ifndef _NETLINK_DUMPER_H_
#define _NETLINK_DUMPER_H_
#define NET_START_OBJECT \
{ \
if (json_output) \
jsonw_start_object(json_wtr); \
}
#define NET_START_OBJECT_NESTED(name) \
{ \
if (json_output) { \
jsonw_name(json_wtr, name); \
jsonw_start_object(json_wtr); \
} else { \
fprintf(stdout, "%s {", name); \
} \
}
#define NET_START_OBJECT_NESTED2 \
{ \
if (json_output) \
jsonw_start_object(json_wtr); \
else \
fprintf(stdout, "{"); \
}
#define NET_END_OBJECT_NESTED \
{ \
if (json_output) \
jsonw_end_object(json_wtr); \
else \
fprintf(stdout, "}"); \
}
#define NET_END_OBJECT \
{ \
if (json_output) \
jsonw_end_object(json_wtr); \
}
#define NET_END_OBJECT_FINAL \
{ \
if (json_output) \
jsonw_end_object(json_wtr); \
else \
fprintf(stdout, "\n"); \
}
#define NET_START_ARRAY(name, fmt_str) \
{ \
if (json_output) { \
jsonw_name(json_wtr, name); \
jsonw_start_array(json_wtr); \
} else { \
fprintf(stdout, fmt_str, name); \
} \
}
#define NET_END_ARRAY(endstr) \
{ \
if (json_output) \
jsonw_end_array(json_wtr); \
else \
fprintf(stdout, "%s", endstr); \
}
#define NET_DUMP_UINT(name, fmt_str, val) \
{ \
if (json_output) \
jsonw_uint_field(json_wtr, name, val); \
else \
fprintf(stdout, fmt_str, val); \
}
#define NET_DUMP_UINT_ONLY(str) \
{ \
if (json_output) \
jsonw_uint(json_wtr, str); \
else \
fprintf(stdout, "%u ", str); \
}
#define NET_DUMP_STR(name, fmt_str, str) \
{ \
if (json_output) \
jsonw_string_field(json_wtr, name, str);\
else \
fprintf(stdout, fmt_str, str); \
}
#define NET_DUMP_STR_ONLY(str) \
{ \
if (json_output) \
jsonw_string(json_wtr, str); \
else \
fprintf(stdout, "%s ", str); \
}
#endif
+258
View File
@@ -0,0 +1,258 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (C) 2018 Facebook
// Author: Yonghong Song <yhs@fb.com>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <bpf/bpf.h>
#include "main.h"
/* 0: undecided, 1: supported, 2: not supported */
static int perf_query_supported;
static bool has_perf_query_support(void)
{
__u64 probe_offset, probe_addr;
__u32 len, prog_id, fd_type;
char buf[256];
int fd;
if (perf_query_supported)
goto out;
fd = open("/", O_RDONLY);
if (fd < 0) {
p_err("perf_query_support: cannot open directory \"/\" (%s)",
strerror(errno));
goto out;
}
/* the following query will fail as no bpf attachment,
* the expected errno is ENOTSUPP
*/
errno = 0;
len = sizeof(buf);
bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id,
&fd_type, &probe_offset, &probe_addr);
if (errno == 524 /* ENOTSUPP */) {
perf_query_supported = 1;
goto close_fd;
}
perf_query_supported = 2;
p_err("perf_query_support: %s", strerror(errno));
fprintf(stderr,
"HINT: non root or kernel doesn't support TASK_FD_QUERY\n");
close_fd:
close(fd);
out:
return perf_query_supported == 1;
}
static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
char *buf, __u64 probe_offset, __u64 probe_addr)
{
jsonw_start_object(json_wtr);
jsonw_int_field(json_wtr, "pid", pid);
jsonw_int_field(json_wtr, "fd", fd);
jsonw_uint_field(json_wtr, "prog_id", prog_id);
switch (fd_type) {
case BPF_FD_TYPE_RAW_TRACEPOINT:
jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint");
jsonw_string_field(json_wtr, "tracepoint", buf);
break;
case BPF_FD_TYPE_TRACEPOINT:
jsonw_string_field(json_wtr, "fd_type", "tracepoint");
jsonw_string_field(json_wtr, "tracepoint", buf);
break;
case BPF_FD_TYPE_KPROBE:
jsonw_string_field(json_wtr, "fd_type", "kprobe");
if (buf[0] != '\0') {
jsonw_string_field(json_wtr, "func", buf);
jsonw_lluint_field(json_wtr, "offset", probe_offset);
} else {
jsonw_lluint_field(json_wtr, "addr", probe_addr);
}
break;
case BPF_FD_TYPE_KRETPROBE:
jsonw_string_field(json_wtr, "fd_type", "kretprobe");
if (buf[0] != '\0') {
jsonw_string_field(json_wtr, "func", buf);
jsonw_lluint_field(json_wtr, "offset", probe_offset);
} else {
jsonw_lluint_field(json_wtr, "addr", probe_addr);
}
break;
case BPF_FD_TYPE_UPROBE:
jsonw_string_field(json_wtr, "fd_type", "uprobe");
jsonw_string_field(json_wtr, "filename", buf);
jsonw_lluint_field(json_wtr, "offset", probe_offset);
break;
case BPF_FD_TYPE_URETPROBE:
jsonw_string_field(json_wtr, "fd_type", "uretprobe");
jsonw_string_field(json_wtr, "filename", buf);
jsonw_lluint_field(json_wtr, "offset", probe_offset);
break;
default:
break;
}
jsonw_end_object(json_wtr);
}
static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
char *buf, __u64 probe_offset, __u64 probe_addr)
{
printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id);
switch (fd_type) {
case BPF_FD_TYPE_RAW_TRACEPOINT:
printf("raw_tracepoint %s\n", buf);
break;
case BPF_FD_TYPE_TRACEPOINT:
printf("tracepoint %s\n", buf);
break;
case BPF_FD_TYPE_KPROBE:
if (buf[0] != '\0')
printf("kprobe func %s offset %llu\n", buf,
probe_offset);
else
printf("kprobe addr %llu\n", probe_addr);
break;
case BPF_FD_TYPE_KRETPROBE:
if (buf[0] != '\0')
printf("kretprobe func %s offset %llu\n", buf,
probe_offset);
else
printf("kretprobe addr %llu\n", probe_addr);
break;
case BPF_FD_TYPE_UPROBE:
printf("uprobe filename %s offset %llu\n", buf, probe_offset);
break;
case BPF_FD_TYPE_URETPROBE:
printf("uretprobe filename %s offset %llu\n", buf,
probe_offset);
break;
default:
break;
}
}
static int show_proc(void)
{
struct dirent *proc_de, *pid_fd_de;
__u64 probe_offset, probe_addr;
__u32 len, prog_id, fd_type;
DIR *proc, *pid_fd;
int err, pid, fd;
const char *pch;
char buf[4096];
proc = opendir("/proc");
if (!proc)
return -1;
while ((proc_de = readdir(proc))) {
pid = 0;
pch = proc_de->d_name;
/* pid should be all numbers */
while (isdigit(*pch)) {
pid = pid * 10 + *pch - '0';
pch++;
}
if (*pch != '\0')
continue;
err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name);
if (err < 0 || err >= (int)sizeof(buf))
continue;
pid_fd = opendir(buf);
if (!pid_fd)
continue;
while ((pid_fd_de = readdir(pid_fd))) {
fd = 0;
pch = pid_fd_de->d_name;
/* fd should be all numbers */
while (isdigit(*pch)) {
fd = fd * 10 + *pch - '0';
pch++;
}
if (*pch != '\0')
continue;
/* query (pid, fd) for potential perf events */
len = sizeof(buf);
err = bpf_task_fd_query(pid, fd, 0, buf, &len,
&prog_id, &fd_type,
&probe_offset, &probe_addr);
if (err < 0)
continue;
if (json_output)
print_perf_json(pid, fd, prog_id, fd_type, buf,
probe_offset, probe_addr);
else
print_perf_plain(pid, fd, prog_id, fd_type, buf,
probe_offset, probe_addr);
}
closedir(pid_fd);
}
closedir(proc);
return 0;
}
static int do_show(int argc, char **argv)
{
int err;
if (!has_perf_query_support())
return -1;
if (json_output)
jsonw_start_array(json_wtr);
err = show_proc();
if (json_output)
jsonw_end_array(json_wtr);
return err;
}
static int do_help(int argc, char **argv)
{
fprintf(stderr,
"Usage: %1$s %2$s { show | list }\n"
" %1$s %2$s help\n"
"\n"
" " HELP_SPEC_OPTIONS " }\n"
"",
bin_name, argv[-2]);
return 0;
}
static const struct cmd cmds[] = {
{ "show", do_show },
{ "list", do_show },
{ "help", do_help },
{ 0 }
};
int do_perf(int argc, char **argv)
{
return cmd_select(cmds, argc, argv, do_help);
}
+263
View File
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2020 Facebook */
#include <errno.h>
#include <linux/err.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include <bpf/hashmap.h>
#include "main.h"
#include "skeleton/pid_iter.h"
#ifdef BPFTOOL_WITHOUT_SKELETONS
int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type)
{
return -ENOTSUP;
}
void delete_obj_refs_table(struct hashmap *map) {}
void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix) {}
void emit_obj_refs_json(struct hashmap *map, __u32 id, json_writer_t *json_writer) {}
#else /* BPFTOOL_WITHOUT_SKELETONS */
#include "pid_iter.skel.h"
static void add_ref(struct hashmap *map, struct pid_iter_entry *e)
{
struct hashmap_entry *entry;
struct obj_refs *refs;
struct obj_ref *ref;
int err, i;
void *tmp;
hashmap__for_each_key_entry(map, entry, e->id) {
refs = entry->pvalue;
for (i = 0; i < refs->ref_cnt; i++) {
if (refs->refs[i].pid == e->pid)
return;
}
tmp = realloc(refs->refs, (refs->ref_cnt + 1) * sizeof(*ref));
if (!tmp) {
p_err("failed to re-alloc memory for ID %u, PID %d, COMM %s...",
e->id, e->pid, e->comm);
return;
}
refs->refs = tmp;
ref = &refs->refs[refs->ref_cnt];
ref->pid = e->pid;
memcpy(ref->comm, e->comm, sizeof(ref->comm));
ref->comm[sizeof(ref->comm) - 1] = '\0';
refs->ref_cnt++;
return;
}
/* new ref */
refs = calloc(1, sizeof(*refs));
if (!refs) {
p_err("failed to alloc memory for ID %u, PID %d, COMM %s...",
e->id, e->pid, e->comm);
return;
}
refs->refs = malloc(sizeof(*refs->refs));
if (!refs->refs) {
free(refs);
p_err("failed to alloc memory for ID %u, PID %d, COMM %s...",
e->id, e->pid, e->comm);
return;
}
ref = &refs->refs[0];
ref->pid = e->pid;
memcpy(ref->comm, e->comm, sizeof(ref->comm));
ref->comm[sizeof(ref->comm) - 1] = '\0';
refs->ref_cnt = 1;
refs->has_bpf_cookie = e->has_bpf_cookie;
refs->bpf_cookie = e->bpf_cookie;
err = hashmap__append(map, e->id, refs);
if (err)
p_err("failed to append entry to hashmap for ID %u: %s",
e->id, strerror(errno));
}
static int __printf(2, 0)
libbpf_print_none(__maybe_unused enum libbpf_print_level level,
__maybe_unused const char *format,
__maybe_unused va_list args)
{
return 0;
}
int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type)
{
struct pid_iter_entry *e;
char buf[4096 / sizeof(*e) * sizeof(*e)];
struct pid_iter_bpf *skel;
int err, ret, fd = -1, i;
*map = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL);
if (IS_ERR(*map)) {
p_err("failed to create hashmap for PID references");
return -1;
}
set_max_rlimit();
skel = pid_iter_bpf__open();
if (!skel) {
p_err("failed to open PID iterator skeleton");
return -1;
}
skel->rodata->obj_type = type;
if (!verifier_logs) {
libbpf_print_fn_t default_print;
/* Unless debug information is on, we don't want the output to
* be polluted with libbpf errors if bpf_iter is not supported.
*/
default_print = libbpf_set_print(libbpf_print_none);
err = pid_iter_bpf__load(skel);
libbpf_set_print(default_print);
} else {
err = pid_iter_bpf__load(skel);
}
if (err) {
/* too bad, kernel doesn't support BPF iterators yet */
err = 0;
goto out;
}
err = pid_iter_bpf__attach(skel);
if (err) {
/* if we loaded above successfully, attach has to succeed */
p_err("failed to attach PID iterator: %d", err);
goto out;
}
fd = bpf_iter_create(bpf_link__fd(skel->links.iter));
if (fd < 0) {
err = -errno;
p_err("failed to create PID iterator session: %d", err);
goto out;
}
while (true) {
ret = read(fd, buf, sizeof(buf));
if (ret < 0) {
if (errno == EAGAIN)
continue;
err = -errno;
p_err("failed to read PID iterator output: %d", err);
goto out;
}
if (ret == 0)
break;
if (ret % sizeof(*e)) {
err = -EINVAL;
p_err("invalid PID iterator output format");
goto out;
}
ret /= sizeof(*e);
e = (void *)buf;
for (i = 0; i < ret; i++, e++) {
add_ref(*map, e);
}
}
err = 0;
out:
if (fd >= 0)
close(fd);
pid_iter_bpf__destroy(skel);
return err;
}
void delete_obj_refs_table(struct hashmap *map)
{
struct hashmap_entry *entry;
size_t bkt;
if (!map)
return;
hashmap__for_each_entry(map, entry, bkt) {
struct obj_refs *refs = entry->pvalue;
free(refs->refs);
free(refs);
}
hashmap__free(map);
}
void emit_obj_refs_json(struct hashmap *map, __u32 id,
json_writer_t *json_writer)
{
struct hashmap_entry *entry;
if (hashmap__empty(map))
return;
hashmap__for_each_key_entry(map, entry, id) {
struct obj_refs *refs = entry->pvalue;
int i;
if (refs->ref_cnt == 0)
break;
if (refs->has_bpf_cookie)
jsonw_lluint_field(json_writer, "bpf_cookie", refs->bpf_cookie);
jsonw_name(json_writer, "pids");
jsonw_start_array(json_writer);
for (i = 0; i < refs->ref_cnt; i++) {
struct obj_ref *ref = &refs->refs[i];
jsonw_start_object(json_writer);
jsonw_int_field(json_writer, "pid", ref->pid);
jsonw_string_field(json_writer, "comm", ref->comm);
jsonw_end_object(json_writer);
}
jsonw_end_array(json_writer);
break;
}
}
void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix)
{
struct hashmap_entry *entry;
if (hashmap__empty(map))
return;
hashmap__for_each_key_entry(map, entry, id) {
struct obj_refs *refs = entry->pvalue;
int i;
if (refs->ref_cnt == 0)
break;
if (refs->has_bpf_cookie)
printf("\n\tbpf_cookie %llu", (unsigned long long) refs->bpf_cookie);
printf("%s", prefix);
for (i = 0; i < refs->ref_cnt; i++) {
struct obj_ref *ref = &refs->refs[i];
printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid);
}
break;
}
}
#endif
File diff suppressed because it is too large Load Diff
+217
View File
@@ -0,0 +1,217 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/*
* Copyright (C) 2025 Google LLC.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <err.h>
#include <openssl/opensslv.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/cms.h>
#include <linux/keyctl.h>
#include <errno.h>
#include <bpf/skel_internal.h>
#include "main.h"
#define OPEN_SSL_ERR_BUF_LEN 256
/* Use deprecated in 3.0 ERR_get_error_line_data for openssl < 3 */
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
#define ERR_get_error_all(file, line, func, data, flags) \
ERR_get_error_line_data(file, line, data, flags)
#endif
static void display_openssl_errors(int l)
{
char buf[OPEN_SSL_ERR_BUF_LEN];
const char *file;
const char *data;
unsigned long e;
int flags;
int line;
while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
ERR_error_string_n(e, buf, sizeof(buf));
if (data && (flags & ERR_TXT_STRING)) {
p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
} else {
p_err("OpenSSL %s: %s:%d", buf, file, line);
}
}
}
#define DISPLAY_OSSL_ERR(cond) \
do { \
bool __cond = (cond); \
if (__cond && ERR_peek_error()) \
display_openssl_errors(__LINE__);\
} while (0)
static EVP_PKEY *read_private_key(const char *pkey_path)
{
EVP_PKEY *private_key = NULL;
BIO *b;
b = BIO_new_file(pkey_path, "rb");
private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
BIO_free(b);
DISPLAY_OSSL_ERR(!private_key);
return private_key;
}
static X509 *read_x509(const char *x509_name)
{
unsigned char buf[2];
X509 *x509 = NULL;
BIO *b;
int n;
b = BIO_new_file(x509_name, "rb");
if (!b)
goto cleanup;
/* Look at the first two bytes of the file to determine the encoding */
n = BIO_read(b, buf, 2);
if (n != 2)
goto cleanup;
if (BIO_reset(b) != 0)
goto cleanup;
if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
/* Assume raw DER encoded X.509 */
x509 = d2i_X509_bio(b, NULL);
else
/* Assume PEM encoded X.509 */
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
cleanup:
BIO_free(b);
DISPLAY_OSSL_ERR(!x509);
return x509;
}
__u32 register_session_key(const char *key_der_path)
{
unsigned char *der_buf = NULL;
X509 *x509 = NULL;
int key_id = -1;
int der_len;
if (!key_der_path)
return key_id;
x509 = read_x509(key_der_path);
if (!x509)
goto cleanup;
der_len = i2d_X509(x509, &der_buf);
if (der_len < 0)
goto cleanup;
key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
(size_t)der_len, KEY_SPEC_SESSION_KEYRING);
cleanup:
X509_free(x509);
OPENSSL_free(der_buf);
DISPLAY_OSSL_ERR(key_id == -1);
return key_id;
}
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
{
BIO *bd_in = NULL, *bd_out = NULL;
EVP_PKEY *private_key = NULL;
CMS_ContentInfo *cms = NULL;
long actual_sig_len = 0;
X509 *x509 = NULL;
int err = 0;
bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
if (!bd_in) {
err = -ENOMEM;
goto cleanup;
}
private_key = read_private_key(private_key_path);
if (!private_key) {
err = -EINVAL;
goto cleanup;
}
x509 = read_x509(cert_path);
if (!x509) {
err = -EINVAL;
goto cleanup;
}
cms = CMS_sign(NULL, NULL, NULL, NULL,
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
CMS_STREAM);
if (!cms) {
err = -EINVAL;
goto cleanup;
}
if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
CMS_USE_KEYID | CMS_NOATTR)) {
err = -EINVAL;
goto cleanup;
}
if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
err = -EIO;
goto cleanup;
}
EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
&opts->excl_prog_hash_sz, EVP_sha256(), NULL);
bd_out = BIO_new(BIO_s_mem());
if (!bd_out) {
err = -ENOMEM;
goto cleanup;
}
if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
err = -EIO;
goto cleanup;
}
actual_sig_len = BIO_get_mem_data(bd_out, NULL);
if (actual_sig_len <= 0) {
err = -EIO;
goto cleanup;
}
if ((size_t)actual_sig_len > opts->signature_sz) {
err = -ENOSPC;
goto cleanup;
}
if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
err = -EIO;
goto cleanup;
}
opts->signature_sz = actual_sig_len;
cleanup:
BIO_free(bd_out);
CMS_ContentInfo_free(cms);
X509_free(x509);
EVP_PKEY_free(private_key);
BIO_free(bd_in);
DISPLAY_OSSL_ERR(err < 0);
return err;
}
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (c) 2020 Facebook */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "pid_iter.h"
/* keep in sync with the definition in main.h */
enum bpf_obj_type {
BPF_OBJ_UNKNOWN,
BPF_OBJ_PROG,
BPF_OBJ_MAP,
BPF_OBJ_LINK,
BPF_OBJ_BTF,
};
struct bpf_perf_link___local {
struct bpf_link link;
struct file *perf_file;
} __attribute__((preserve_access_index));
struct perf_event___local {
u64 bpf_cookie;
} __attribute__((preserve_access_index));
enum bpf_link_type___local {
BPF_LINK_TYPE_PERF_EVENT___local = 7,
};
extern const void bpf_link_fops __ksym;
extern const void bpf_link_fops_poll __ksym __weak;
extern const void bpf_map_fops __ksym;
extern const void bpf_prog_fops __ksym;
extern const void btf_fops __ksym;
const volatile enum bpf_obj_type obj_type = BPF_OBJ_UNKNOWN;
static __always_inline __u32 get_obj_id(void *ent, enum bpf_obj_type type)
{
switch (type) {
case BPF_OBJ_PROG:
return BPF_CORE_READ((struct bpf_prog *)ent, aux, id);
case BPF_OBJ_MAP:
return BPF_CORE_READ((struct bpf_map *)ent, id);
case BPF_OBJ_BTF:
return BPF_CORE_READ((struct btf *)ent, id);
case BPF_OBJ_LINK:
return BPF_CORE_READ((struct bpf_link *)ent, id);
default:
return 0;
}
}
/* could be used only with BPF_LINK_TYPE_PERF_EVENT links */
static __u64 get_bpf_cookie(struct bpf_link *link)
{
struct bpf_perf_link___local *perf_link;
struct perf_event___local *event;
perf_link = container_of(link, struct bpf_perf_link___local, link);
event = BPF_CORE_READ(perf_link, perf_file, private_data);
return BPF_CORE_READ(event, bpf_cookie);
}
SEC("iter/task_file")
int iter(struct bpf_iter__task_file *ctx)
{
struct file *file = ctx->file;
struct task_struct *task = ctx->task;
struct pid_iter_entry e;
const void *fops;
if (!file || !task)
return 0;
switch (obj_type) {
case BPF_OBJ_PROG:
fops = &bpf_prog_fops;
break;
case BPF_OBJ_MAP:
fops = &bpf_map_fops;
break;
case BPF_OBJ_BTF:
fops = &btf_fops;
break;
case BPF_OBJ_LINK:
if (&bpf_link_fops_poll &&
file->f_op == &bpf_link_fops_poll)
fops = &bpf_link_fops_poll;
else
fops = &bpf_link_fops;
break;
default:
return 0;
}
if (file->f_op != fops)
return 0;
__builtin_memset(&e, 0, sizeof(e));
e.pid = task->tgid;
e.id = get_obj_id(file->private_data, obj_type);
if (obj_type == BPF_OBJ_LINK &&
bpf_core_enum_value_exists(enum bpf_link_type___local,
BPF_LINK_TYPE_PERF_EVENT___local)) {
struct bpf_link *link = (struct bpf_link *) file->private_data;
if (BPF_CORE_READ(link, type) == bpf_core_enum_value(enum bpf_link_type___local,
BPF_LINK_TYPE_PERF_EVENT___local)) {
e.has_bpf_cookie = true;
e.bpf_cookie = get_bpf_cookie(link);
}
}
bpf_probe_read_kernel_str(&e.comm, sizeof(e.comm),
task->group_leader->comm);
bpf_seq_write(ctx->meta->seq, &e, sizeof(e));
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (c) 2020 Facebook */
#ifndef __PID_ITER_H
#define __PID_ITER_H
struct pid_iter_entry {
__u32 id;
int pid;
__u64 bpf_cookie;
bool has_bpf_cookie;
char comm[16];
};
#endif
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (c) 2020 Facebook
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct bpf_perf_event_value___local {
__u64 counter;
__u64 enabled;
__u64 running;
} __attribute__((preserve_access_index));
/* map of perf event fds, num_cpu * num_metric entries */
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(int));
} events SEC(".maps");
/* readings at fentry */
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(struct bpf_perf_event_value___local));
} fentry_readings SEC(".maps");
/* accumulated readings */
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(struct bpf_perf_event_value___local));
} accum_readings SEC(".maps");
/* sample counts, one per cpu */
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u64));
} counts SEC(".maps");
const volatile __u32 num_cpu = 1;
const volatile __u32 num_metric = 1;
#define MAX_NUM_METRICS 4
SEC("fentry/XXX")
int BPF_PROG(fentry_XXX)
{
struct bpf_perf_event_value___local *ptrs[MAX_NUM_METRICS];
u32 key = bpf_get_smp_processor_id();
u32 i;
/* look up before reading, to reduce error */
for (i = 0; i < num_metric && i < MAX_NUM_METRICS; i++) {
u32 flag = i;
ptrs[i] = bpf_map_lookup_elem(&fentry_readings, &flag);
if (!ptrs[i])
return 0;
}
for (i = 0; i < num_metric && i < MAX_NUM_METRICS; i++) {
struct bpf_perf_event_value___local reading;
int err;
err = bpf_perf_event_read_value(&events, key, (void *)&reading,
sizeof(reading));
if (err)
return 0;
*(ptrs[i]) = reading;
key += num_cpu;
}
return 0;
}
static inline void
fexit_update_maps(u32 id, struct bpf_perf_event_value___local *after)
{
struct bpf_perf_event_value___local *before, diff;
before = bpf_map_lookup_elem(&fentry_readings, &id);
/* only account samples with a valid fentry_reading */
if (before && before->counter) {
struct bpf_perf_event_value___local *accum;
diff.counter = after->counter - before->counter;
diff.enabled = after->enabled - before->enabled;
diff.running = after->running - before->running;
accum = bpf_map_lookup_elem(&accum_readings, &id);
if (accum) {
accum->counter += diff.counter;
accum->enabled += diff.enabled;
accum->running += diff.running;
}
}
}
SEC("fexit/XXX")
int BPF_PROG(fexit_XXX)
{
struct bpf_perf_event_value___local readings[MAX_NUM_METRICS];
u32 cpu = bpf_get_smp_processor_id();
u32 i, zero = 0;
int err;
u64 *count;
/* read all events before updating the maps, to reduce error */
for (i = 0; i < num_metric && i < MAX_NUM_METRICS; i++) {
err = bpf_perf_event_read_value(&events, cpu + i * num_cpu,
(void *)(readings + i),
sizeof(*readings));
if (err)
return 0;
}
count = bpf_map_lookup_elem(&counts, &zero);
if (count) {
*count += 1;
for (i = 0; i < num_metric && i < MAX_NUM_METRICS; i++)
fexit_update_maps(i, &readings[i]);
}
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
+648
View File
@@ -0,0 +1,648 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2020 Facebook */
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/err.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include "json_writer.h"
#include "main.h"
#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
static const struct btf_type *map_info_type;
static __u32 map_info_alloc_len;
static struct btf *btf_vmlinux;
static __s32 map_info_type_id;
struct res {
unsigned int nr_maps;
unsigned int nr_errs;
};
static const struct btf *get_btf_vmlinux(void)
{
if (btf_vmlinux)
return btf_vmlinux;
btf_vmlinux = libbpf_find_kernel_btf();
if (!btf_vmlinux)
p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
return btf_vmlinux;
}
static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
{
const struct btf *kern_btf;
const struct btf_type *t;
const char *st_ops_name;
kern_btf = get_btf_vmlinux();
if (!kern_btf)
return "<btf_vmlinux_not_found>";
t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
return st_ops_name;
}
static __s32 get_map_info_type_id(void)
{
const struct btf *kern_btf;
if (map_info_type_id)
return map_info_type_id;
kern_btf = get_btf_vmlinux();
if (!kern_btf)
return 0;
map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
BTF_KIND_STRUCT);
if (map_info_type_id < 0) {
p_err("can't find bpf_map_info from btf_vmlinux");
return map_info_type_id;
}
map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
/* Ensure map_info_alloc() has at least what the bpftool needs */
map_info_alloc_len = map_info_type->size;
if (map_info_alloc_len < sizeof(struct bpf_map_info))
map_info_alloc_len = sizeof(struct bpf_map_info);
return map_info_type_id;
}
/* If the subcmd needs to print out the bpf_map_info,
* it should always call map_info_alloc to allocate
* a bpf_map_info object instead of allocating it
* on the stack.
*
* map_info_alloc() will take the running kernel's btf
* into account. i.e. it will consider the
* sizeof(struct bpf_map_info) of the running kernel.
*
* It will enable the "struct_ops" cmd to print the latest
* "struct bpf_map_info".
*
* [ Recall that "struct_ops" requires the kernel's btf to
* be available ]
*/
static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
{
struct bpf_map_info *info;
if (get_map_info_type_id() < 0)
return NULL;
info = calloc(1, map_info_alloc_len);
if (!info)
p_err("mem alloc failed");
else
*alloc_len = map_info_alloc_len;
return info;
}
/* It iterates all struct_ops maps of the system.
* It returns the fd in "*res_fd" and map_info in "*info".
* In the very first iteration, info->id should be 0.
* An optional map "*name" filter can be specified.
* The filter can be made more flexible in the future.
* e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc.
*
* Return value:
* 1: A struct_ops map found. It is returned in "*res_fd" and "*info".
* The caller can continue to call get_next in the future.
* 0: No struct_ops map is returned.
* All struct_ops map has been found.
* -1: Error and the caller should abort the iteration.
*/
static int get_next_struct_ops_map(const char *name, int *res_fd,
struct bpf_map_info *info, __u32 info_len)
{
__u32 id = info->id;
int err, fd;
while (true) {
err = bpf_map_get_next_id(id, &id);
if (err) {
if (errno == ENOENT)
return 0;
p_err("can't get next map: %s", strerror(errno));
return -1;
}
fd = bpf_map_get_fd_by_id(id);
if (fd < 0) {
if (errno == ENOENT)
continue;
p_err("can't get map by id (%u): %s",
id, strerror(errno));
return -1;
}
err = bpf_map_get_info_by_fd(fd, info, &info_len);
if (err) {
p_err("can't get map info: %s", strerror(errno));
close(fd);
return -1;
}
if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
(!name || !strcmp(name, info->name))) {
*res_fd = fd;
return 1;
}
close(fd);
}
}
static int cmd_retval(const struct res *res, bool must_have_one_map)
{
if (res->nr_errs || (!res->nr_maps && must_have_one_map))
return -1;
return 0;
}
/* "data" is the work_func private storage */
typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
struct json_writer *wtr);
/* Find all struct_ops map in the system.
* Filter out by "name" (if specified).
* Then call "func(fd, info, data, wtr)" on each struct_ops map found.
*/
static struct res do_search(const char *name, work_func func, void *data,
struct json_writer *wtr)
{
struct bpf_map_info *info;
struct res res = {};
__u32 info_len;
int fd, err;
info = map_info_alloc(&info_len);
if (!info) {
res.nr_errs++;
return res;
}
if (wtr)
jsonw_start_array(wtr);
while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
res.nr_maps++;
err = func(fd, info, data, wtr);
if (err)
res.nr_errs++;
close(fd);
}
if (wtr)
jsonw_end_array(wtr);
if (err)
res.nr_errs++;
if (!wtr && name && !res.nr_errs && !res.nr_maps)
/* It is not printing empty [].
* Thus, needs to specifically say nothing found
* for "name" here.
*/
p_err("no struct_ops found for %s", name);
else if (!wtr && json_output && !res.nr_errs)
/* The "func()" above is not writing any json (i.e. !wtr
* test here).
*
* However, "-j" is enabled and there is no errs here,
* so call json_null() as the current convention of
* other cmds.
*/
jsonw_null(json_wtr);
free(info);
return res;
}
static struct res do_one_id(const char *id_str, work_func func, void *data,
struct json_writer *wtr)
{
struct bpf_map_info *info;
struct res res = {};
unsigned long id;
__u32 info_len;
char *endptr;
int fd;
id = strtoul(id_str, &endptr, 0);
if (*endptr || !id || id > UINT32_MAX) {
p_err("invalid id %s", id_str);
res.nr_errs++;
return res;
}
fd = bpf_map_get_fd_by_id(id);
if (fd < 0) {
p_err("can't get map by id (%lu): %s", id, strerror(errno));
res.nr_errs++;
return res;
}
info = map_info_alloc(&info_len);
if (!info) {
res.nr_errs++;
goto done;
}
if (bpf_map_get_info_by_fd(fd, info, &info_len)) {
p_err("can't get map info: %s", strerror(errno));
res.nr_errs++;
goto done;
}
if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
p_err("%s id %u is not a struct_ops map", info->name, info->id);
res.nr_errs++;
goto done;
}
res.nr_maps++;
if (wtr)
jsonw_start_array(wtr);
if (func(fd, info, data, wtr))
res.nr_errs++;
else if (!wtr && json_output)
/* The "func()" above is not writing any json (i.e. !wtr
* test here).
*
* However, "-j" is enabled and there is no errs here,
* so call json_null() as the current convention of
* other cmds.
*/
jsonw_null(json_wtr);
if (wtr)
jsonw_end_array(wtr);
done:
free(info);
close(fd);
return res;
}
static struct res do_work_on_struct_ops(const char *search_type,
const char *search_term,
work_func func, void *data,
struct json_writer *wtr)
{
if (search_type) {
if (is_prefix(search_type, "id"))
return do_one_id(search_term, func, data, wtr);
else if (!is_prefix(search_type, "name"))
usage();
}
return do_search(search_term, func, data, wtr);
}
static int __do_show(int fd, const struct bpf_map_info *info, void *data,
struct json_writer *wtr)
{
if (wtr) {
jsonw_start_object(wtr);
jsonw_uint_field(wtr, "id", info->id);
jsonw_string_field(wtr, "name", info->name);
jsonw_string_field(wtr, "kernel_struct_ops",
get_kern_struct_ops_name(info));
jsonw_end_object(wtr);
} else {
printf("%u: %-15s %-32s\n", info->id, info->name,
get_kern_struct_ops_name(info));
}
return 0;
}
static int do_show(int argc, char **argv)
{
const char *search_type = NULL, *search_term = NULL;
struct res res;
if (argc && argc != 2)
usage();
if (argc == 2) {
search_type = GET_ARG();
search_term = GET_ARG();
}
res = do_work_on_struct_ops(search_type, search_term, __do_show,
NULL, json_wtr);
return cmd_retval(&res, !!search_term);
}
static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
struct json_writer *wtr)
{
struct btf_dumper *d = (struct btf_dumper *)data;
const struct btf_type *struct_ops_type;
const struct btf *kern_btf = d->btf;
const char *struct_ops_name;
int zero = 0;
void *value;
/* note: d->jw == wtr */
kern_btf = d->btf;
/* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have
* btf_vmlinux_value_type_id.
*/
struct_ops_type = btf__type_by_id(kern_btf,
info->btf_vmlinux_value_type_id);
struct_ops_name = btf__name_by_offset(kern_btf,
struct_ops_type->name_off);
value = calloc(1, info->value_size);
if (!value) {
p_err("mem alloc failed");
return -1;
}
if (bpf_map_lookup_elem(fd, &zero, value)) {
p_err("can't lookup struct_ops map %s id %u",
info->name, info->id);
free(value);
return -1;
}
jsonw_start_object(wtr);
jsonw_name(wtr, "bpf_map_info");
btf_dumper_type(d, map_info_type_id, (void *)info);
jsonw_end_object(wtr);
jsonw_start_object(wtr);
jsonw_name(wtr, struct_ops_name);
btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
jsonw_end_object(wtr);
free(value);
return 0;
}
static int do_dump(int argc, char **argv)
{
const char *search_type = NULL, *search_term = NULL;
json_writer_t *wtr = json_wtr;
const struct btf *kern_btf;
struct btf_dumper d = {};
struct res res;
if (argc && argc != 2)
usage();
if (argc == 2) {
search_type = GET_ARG();
search_term = GET_ARG();
}
kern_btf = get_btf_vmlinux();
if (!kern_btf)
return -1;
if (!json_output) {
wtr = jsonw_new(stdout);
if (!wtr) {
p_err("can't create json writer");
return -1;
}
jsonw_pretty(wtr, true);
}
d.btf = kern_btf;
d.jw = wtr;
d.is_plain_text = !json_output;
d.prog_id_as_func_ptr = true;
res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
wtr);
if (!json_output)
jsonw_destroy(&wtr);
return cmd_retval(&res, !!search_term);
}
static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
struct json_writer *wtr)
{
int zero = 0;
if (bpf_map_delete_elem(fd, &zero)) {
p_err("can't unload %s %s id %u: %s",
get_kern_struct_ops_name(info), info->name,
info->id, strerror(errno));
return -1;
}
p_info("Unregistered %s %s id %u",
get_kern_struct_ops_name(info), info->name,
info->id);
return 0;
}
static int do_unregister(int argc, char **argv)
{
const char *search_type, *search_term;
struct res res;
if (argc != 2)
usage();
search_type = GET_ARG();
search_term = GET_ARG();
res = do_work_on_struct_ops(search_type, search_term,
__do_unregister, NULL, NULL);
return cmd_retval(&res, true);
}
static int pin_link(struct bpf_link *link, const char *pindir,
const char *name)
{
char pinfile[PATH_MAX];
int err;
err = pathname_concat(pinfile, sizeof(pinfile), pindir, name);
if (err)
return -1;
return bpf_link__pin(link, pinfile);
}
static int do_register(int argc, char **argv)
{
LIBBPF_OPTS(bpf_object_open_opts, open_opts);
__u32 link_info_len = sizeof(struct bpf_link_info);
struct bpf_link_info link_info = {};
struct bpf_map_info info = {};
__u32 info_len = sizeof(info);
int nr_errs = 0, nr_maps = 0;
const char *linkdir = NULL;
struct bpf_object *obj;
struct bpf_link *link;
struct bpf_map *map;
const char *file;
if (argc != 1 && argc != 2)
usage();
file = GET_ARG();
if (argc == 1)
linkdir = GET_ARG();
if (linkdir && create_and_mount_bpffs_dir(linkdir)) {
p_err("can't mount bpffs for pinning");
return -1;
}
if (verifier_logs)
/* log_level1 + log_level2 + stats, but not stable UAPI */
open_opts.kernel_log_level = 1 + 2 + 4;
obj = bpf_object__open_file(file, &open_opts);
if (!obj)
return -1;
set_max_rlimit();
if (bpf_object__load(obj)) {
bpf_object__close(obj);
return -1;
}
bpf_object__for_each_map(map, obj) {
if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
continue;
link = bpf_map__attach_struct_ops(map);
if (!link) {
p_err("can't register struct_ops %s: %s",
bpf_map__name(map), strerror(errno));
nr_errs++;
continue;
}
nr_maps++;
if (bpf_map_get_info_by_fd(bpf_map__fd(map), &info,
&info_len)) {
/* Not p_err. The struct_ops was attached
* successfully.
*/
p_info("Registered %s but can't find id: %s",
bpf_map__name(map), strerror(errno));
goto clean_link;
}
if (!(bpf_map__map_flags(map) & BPF_F_LINK)) {
p_info("Registered %s %s id %u",
get_kern_struct_ops_name(&info),
info.name,
info.id);
goto clean_link;
}
if (bpf_link_get_info_by_fd(bpf_link__fd(link),
&link_info,
&link_info_len)) {
p_err("Registered %s but can't find link id: %s",
bpf_map__name(map), strerror(errno));
nr_errs++;
goto clean_link;
}
if (linkdir && pin_link(link, linkdir, info.name)) {
p_err("can't pin link %u for %s: %s",
link_info.id, info.name,
strerror(errno));
nr_errs++;
goto clean_link;
}
p_info("Registered %s %s map id %u link id %u",
get_kern_struct_ops_name(&info),
info.name, info.id, link_info.id);
clean_link:
bpf_link__disconnect(link);
bpf_link__destroy(link);
}
bpf_object__close(obj);
if (nr_errs)
return -1;
if (!nr_maps) {
p_err("no struct_ops found in %s", file);
return -1;
}
if (json_output)
jsonw_null(json_wtr);
return 0;
}
static int do_help(int argc, char **argv)
{
if (json_output) {
jsonw_null(json_wtr);
return 0;
}
fprintf(stderr,
"Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
" %1$s %2$s dump [STRUCT_OPS_MAP]\n"
" %1$s %2$s register OBJ [LINK_DIR]\n"
" %1$s %2$s unregister STRUCT_OPS_MAP\n"
" %1$s %2$s help\n"
"\n"
" STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
" " HELP_SPEC_OPTIONS " }\n"
"",
bin_name, argv[-2]);
return 0;
}
static const struct cmd cmds[] = {
{ "show", do_show },
{ "list", do_show },
{ "register", do_register },
{ "unregister", do_unregister },
{ "dump", do_dump },
{ "help", do_help },
{ 0 }
};
int do_struct_ops(int argc, char **argv)
{
int err;
err = cmd_select(cmds, argc, argv, do_help);
btf__free(btf_vmlinux);
return err;
}
+210
View File
@@ -0,0 +1,210 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2025 Didi Technology Co., Tao Chen */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mntent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "json_writer.h"
#include "main.h"
#define MOUNTS_FILE "/proc/mounts"
static struct {
const char *header;
const char *key;
} sets[] = {
{"allowed_cmds", "delegate_cmds"},
{"allowed_maps", "delegate_maps"},
{"allowed_progs", "delegate_progs"},
{"allowed_attachs", "delegate_attachs"},
};
static bool has_delegate_options(const char *mnt_ops)
{
return strstr(mnt_ops, "delegate_cmds") ||
strstr(mnt_ops, "delegate_maps") ||
strstr(mnt_ops, "delegate_progs") ||
strstr(mnt_ops, "delegate_attachs");
}
static char *get_delegate_value(char *opts, const char *key)
{
char *token, *rest, *ret = NULL;
if (!opts)
return NULL;
for (token = strtok_r(opts, ",", &rest); token;
token = strtok_r(NULL, ",", &rest)) {
if (strncmp(token, key, strlen(key)) == 0 &&
token[strlen(key)] == '=') {
ret = token + strlen(key) + 1;
break;
}
}
return ret;
}
static void print_items_per_line(char *input, int items_per_line)
{
char *str, *rest;
int cnt = 0;
if (!input)
return;
for (str = strtok_r(input, ":", &rest); str;
str = strtok_r(NULL, ":", &rest)) {
if (cnt % items_per_line == 0)
printf("\n\t ");
printf("%-20s", str);
cnt++;
}
}
#define ITEMS_PER_LINE 4
static void show_token_info_plain(struct mntent *mntent)
{
size_t i;
printf("token_info %s", mntent->mnt_dir);
for (i = 0; i < ARRAY_SIZE(sets); i++) {
char *opts, *value;
printf("\n\t%s:", sets[i].header);
opts = strdup(mntent->mnt_opts);
value = get_delegate_value(opts, sets[i].key);
print_items_per_line(value, ITEMS_PER_LINE);
free(opts);
}
printf("\n");
}
static void split_json_array_str(char *input)
{
char *str, *rest;
if (!input) {
jsonw_start_array(json_wtr);
jsonw_end_array(json_wtr);
return;
}
jsonw_start_array(json_wtr);
for (str = strtok_r(input, ":", &rest); str;
str = strtok_r(NULL, ":", &rest)) {
jsonw_string(json_wtr, str);
}
jsonw_end_array(json_wtr);
}
static void show_token_info_json(struct mntent *mntent)
{
size_t i;
jsonw_start_object(json_wtr);
jsonw_string_field(json_wtr, "token_info", mntent->mnt_dir);
for (i = 0; i < ARRAY_SIZE(sets); i++) {
char *opts, *value;
jsonw_name(json_wtr, sets[i].header);
opts = strdup(mntent->mnt_opts);
value = get_delegate_value(opts, sets[i].key);
split_json_array_str(value);
free(opts);
}
jsonw_end_object(json_wtr);
}
static int __show_token_info(struct mntent *mntent)
{
if (json_output)
show_token_info_json(mntent);
else
show_token_info_plain(mntent);
return 0;
}
static int show_token_info(void)
{
FILE *fp;
struct mntent *ent;
fp = setmntent(MOUNTS_FILE, "r");
if (!fp) {
p_err("Failed to open: %s", MOUNTS_FILE);
return -1;
}
if (json_output)
jsonw_start_array(json_wtr);
while ((ent = getmntent(fp)) != NULL) {
if (strncmp(ent->mnt_type, "bpf", 3) == 0) {
if (has_delegate_options(ent->mnt_opts))
__show_token_info(ent);
}
}
if (json_output)
jsonw_end_array(json_wtr);
endmntent(fp);
return 0;
}
static int do_show(int argc, char **argv)
{
if (argc)
return BAD_ARG();
return show_token_info();
}
static int do_help(int argc, char **argv)
{
if (json_output) {
jsonw_null(json_wtr);
return 0;
}
fprintf(stderr,
"Usage: %1$s %2$s { show | list }\n"
" %1$s %2$s help\n"
" " HELP_SPEC_OPTIONS " }\n"
"\n"
"",
bin_name, argv[-2]);
return 0;
}
static const struct cmd cmds[] = {
{ "show", do_show },
{ "list", do_show },
{ "help", do_help },
{ 0 }
};
int do_token(int argc, char **argv)
{
return cmd_select(cmds, argc, argv, do_help);
}
+159
View File
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (c) 2015-2017 Daniel Borkmann */
/* Copyright (c) 2018 Netronome Systems, Inc. */
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/magic.h>
#include <fcntl.h>
#include <sys/vfs.h>
#include "main.h"
#ifndef TRACEFS_MAGIC
# define TRACEFS_MAGIC 0x74726163
#endif
#define _textify(x) #x
#define textify(x) _textify(x)
FILE *trace_pipe_fd;
char *buff;
static int validate_tracefs_mnt(const char *mnt, unsigned long magic)
{
struct statfs st_fs;
if (statfs(mnt, &st_fs) < 0)
return -ENOENT;
if ((unsigned long)st_fs.f_type != magic)
return -ENOENT;
return 0;
}
static bool
find_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt)
{
size_t src_len;
if (validate_tracefs_mnt(mntpt, magic))
return false;
src_len = strlen(mntpt);
if (src_len + 1 >= PATH_MAX) {
p_err("tracefs mount point name too long");
return false;
}
strcpy(mnt, mntpt);
return true;
}
static bool get_tracefs_pipe(char *mnt)
{
static const char * const known_mnts[] = {
"/sys/kernel/tracing",
"/sys/kernel/debug/tracing",
};
const char *pipe_name = "/trace_pipe";
const char *fstype = "tracefs";
char type[100], format[32];
const char * const *ptr;
bool found = false;
FILE *fp;
for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++)
if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, *ptr))
goto exit_found;
fp = fopen("/proc/mounts", "r");
if (!fp)
return false;
/* Allow room for NULL terminating byte and pipe file name */
snprintf(format, sizeof(format), "%%*s %%%zus %%99s %%*s %%*d %%*d\\n",
PATH_MAX - strlen(pipe_name) - 1);
while (fscanf(fp, format, mnt, type) == 2)
if (strcmp(type, fstype) == 0) {
found = true;
break;
}
fclose(fp);
/* The string from fscanf() might be truncated, check mnt is valid */
if (found && validate_tracefs_mnt(mnt, TRACEFS_MAGIC))
goto exit_found;
if (block_mount)
return false;
p_info("could not find tracefs, attempting to mount it now");
strcpy(mnt, known_mnts[0]);
if (mount_tracefs(mnt))
return false;
exit_found:
strcat(mnt, pipe_name);
return true;
}
static void exit_tracelog(int signum)
{
fclose(trace_pipe_fd);
free(buff);
if (json_output) {
jsonw_end_array(json_wtr);
jsonw_destroy(&json_wtr);
}
exit(0);
}
int do_tracelog(int argc, char **argv)
{
const struct sigaction act = {
.sa_handler = exit_tracelog
};
char trace_pipe[PATH_MAX];
size_t buff_len = 0;
if (json_output)
jsonw_start_array(json_wtr);
if (!get_tracefs_pipe(trace_pipe))
return -1;
trace_pipe_fd = fopen(trace_pipe, "r");
if (!trace_pipe_fd) {
p_err("could not open trace pipe: %s", strerror(errno));
return -1;
}
sigaction(SIGHUP, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
while (1) {
ssize_t ret;
ret = getline(&buff, &buff_len, trace_pipe_fd);
if (ret <= 0) {
p_err("failed to read content from trace pipe: %s",
strerror(errno));
break;
}
if (json_output)
jsonw_string(json_wtr, buff);
else
printf("%s", buff);
}
fclose(trace_pipe_fd);
free(buff);
return -1;
}
@@ -0,0 +1,434 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2018 Netronome Systems, Inc. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <bpf/libbpf.h>
#include <bpf/libbpf_internal.h>
#include "disasm.h"
#include "json_writer.h"
#include "main.h"
#include "xlated_dumper.h"
static int kernel_syms_cmp(const void *sym_a, const void *sym_b)
{
return ((struct kernel_sym *)sym_a)->address -
((struct kernel_sym *)sym_b)->address;
}
void kernel_syms_load(struct dump_data *dd)
{
struct kernel_sym *sym;
char buff[256];
void *tmp, *address;
FILE *fp;
fp = fopen("/proc/kallsyms", "r");
if (!fp)
return;
while (fgets(buff, sizeof(buff), fp)) {
tmp = libbpf_reallocarray(dd->sym_mapping, dd->sym_count + 1,
sizeof(*dd->sym_mapping));
if (!tmp) {
out:
free(dd->sym_mapping);
dd->sym_mapping = NULL;
fclose(fp);
return;
}
dd->sym_mapping = tmp;
sym = &dd->sym_mapping[dd->sym_count];
/* module is optional */
sym->module[0] = '\0';
/* trim the square brackets around the module name */
if (sscanf(buff, "%p %*c %s [%[^]]s", &address, sym->name, sym->module) < 2)
continue;
sym->address = (unsigned long)address;
if (!strcmp(sym->name, "__bpf_call_base")) {
dd->address_call_base = sym->address;
/* sysctl kernel.kptr_restrict was set */
if (!sym->address)
goto out;
}
if (sym->address)
dd->sym_count++;
}
fclose(fp);
qsort(dd->sym_mapping, dd->sym_count,
sizeof(*dd->sym_mapping), kernel_syms_cmp);
}
void kernel_syms_destroy(struct dump_data *dd)
{
free(dd->sym_mapping);
}
struct kernel_sym *kernel_syms_search(struct dump_data *dd,
unsigned long key)
{
struct kernel_sym sym = {
.address = key,
};
return dd->sym_mapping ?
bsearch(&sym, dd->sym_mapping, dd->sym_count,
sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL;
}
static void __printf(2, 3) print_insn(void *private_data, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
static void __printf(2, 3)
print_insn_for_graph(void *private_data, const char *fmt, ...)
{
char buf[64], *p;
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
p = buf;
while (*p != '\0') {
if (*p == '\n') {
memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
/* Align each instruction dump row left. */
*p++ = '\\';
*p++ = 'l';
/* Output multiline concatenation. */
*p++ = '\\';
} else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
/* Escape special character. */
*p++ = '\\';
}
p++;
}
printf("%s", buf);
}
static void __printf(2, 3)
print_insn_json(void *private_data, const char *fmt, ...)
{
unsigned int l = strlen(fmt);
char chomped_fmt[l];
va_list args;
va_start(args, fmt);
if (l > 0) {
strncpy(chomped_fmt, fmt, l - 1);
chomped_fmt[l - 1] = '\0';
}
jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
va_end(args);
}
static const char *print_call_pcrel(struct dump_data *dd,
struct kernel_sym *sym,
unsigned long address,
const struct bpf_insn *insn)
{
if (!dd->nr_jited_ksyms)
/* Do not show address for interpreted programs */
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"%+d", insn->off);
else if (sym)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"%+d#%s", insn->off, sym->name);
else
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"%+d#0x%lx", insn->off, address);
return dd->scratch_buff;
}
static const char *print_call_helper(struct dump_data *dd,
struct kernel_sym *sym,
unsigned long address)
{
if (sym)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"%s", sym->name);
else
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"0x%lx", address);
return dd->scratch_buff;
}
static const char *print_call(void *private_data,
const struct bpf_insn *insn)
{
struct dump_data *dd = private_data;
unsigned long address = dd->address_call_base + insn->imm;
struct kernel_sym *sym;
if (insn->src_reg == BPF_PSEUDO_CALL &&
(__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
address = dd->jited_ksyms[insn->imm];
sym = kernel_syms_search(dd, address);
if (insn->src_reg == BPF_PSEUDO_CALL)
return print_call_pcrel(dd, sym, address, insn);
else
return print_call_helper(dd, sym, address);
}
static const char *print_imm(void *private_data,
const struct bpf_insn *insn,
__u64 full_imm)
{
struct dump_data *dd = private_data;
if (insn->src_reg == BPF_PSEUDO_MAP_FD)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"map[id:%d]", insn->imm);
else if (insn->src_reg == BPF_PSEUDO_MAP_VALUE)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"map[id:%d][0]+%d", insn->imm, (insn + 1)->imm);
else if (insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"map[idx:%d]+%d", insn->imm, (insn + 1)->imm);
else if (insn->src_reg == BPF_PSEUDO_FUNC)
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"subprog[%+d]", insn->imm);
else
snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
"0x%llx", (unsigned long long)full_imm);
return dd->scratch_buff;
}
void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum)
{
const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
const struct bpf_insn_cbs cbs = {
.cb_print = print_insn_json,
.cb_call = print_call,
.cb_imm = print_imm,
.private_data = dd,
};
struct bpf_func_info *record;
struct bpf_insn *insn = buf;
struct btf *btf = dd->btf;
bool double_insn = false;
unsigned int nr_skip = 0;
char func_sig[1024];
unsigned int i;
jsonw_start_array(json_wtr);
record = dd->func_info;
for (i = 0; i < len / sizeof(*insn); i++) {
if (double_insn) {
double_insn = false;
continue;
}
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
jsonw_start_object(json_wtr);
if (btf && record) {
if (record->insn_off == i) {
btf_dumper_type_only(btf, record->type_id,
func_sig,
sizeof(func_sig));
if (func_sig[0] != '\0') {
jsonw_name(json_wtr, "proto");
jsonw_string(json_wtr, func_sig);
}
record = (void *)record + dd->finfo_rec_size;
}
}
if (prog_linfo) {
const struct bpf_line_info *linfo;
linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
if (linfo) {
btf_dump_linfo_json(btf, linfo, linum);
nr_skip++;
}
}
jsonw_name(json_wtr, "disasm");
print_bpf_insn(&cbs, insn + i, true);
if (opcodes) {
jsonw_name(json_wtr, "opcodes");
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "code");
jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
jsonw_name(json_wtr, "src_reg");
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
jsonw_name(json_wtr, "dst_reg");
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
jsonw_name(json_wtr, "off");
print_hex_data_json((uint8_t *)(&insn[i].off), 2);
jsonw_name(json_wtr, "imm");
if (double_insn && i < len - 1)
print_hex_data_json((uint8_t *)(&insn[i].imm),
12);
else
print_hex_data_json((uint8_t *)(&insn[i].imm),
4);
jsonw_end_object(json_wtr);
}
jsonw_end_object(json_wtr);
}
jsonw_end_array(json_wtr);
}
void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum)
{
const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
const struct bpf_insn_cbs cbs = {
.cb_print = print_insn,
.cb_call = print_call,
.cb_imm = print_imm,
.private_data = dd,
};
struct bpf_func_info *record;
struct bpf_insn *insn = buf;
struct btf *btf = dd->btf;
unsigned int nr_skip = 0;
bool double_insn = false;
char func_sig[1024];
unsigned int i;
record = dd->func_info;
for (i = 0; i < len / sizeof(*insn); i++) {
if (double_insn) {
double_insn = false;
continue;
}
if (btf && record) {
if (record->insn_off == i) {
btf_dumper_type_only(btf, record->type_id,
func_sig,
sizeof(func_sig));
if (func_sig[0] != '\0')
printf("%s:\n", func_sig);
record = (void *)record + dd->finfo_rec_size;
}
}
if (prog_linfo) {
const struct bpf_line_info *linfo;
linfo = bpf_prog_linfo__lfind(prog_linfo, i, nr_skip);
if (linfo) {
btf_dump_linfo_plain(btf, linfo, "; ",
linum);
nr_skip++;
}
}
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
printf("%4u: ", i);
print_bpf_insn(&cbs, insn + i, true);
if (opcodes) {
printf(" ");
fprint_hex(stdout, insn + i, 8, " ");
if (double_insn && i < len - 1) {
printf(" ");
fprint_hex(stdout, insn + i + 1, 8, " ");
}
printf("\n");
}
}
}
void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
unsigned int start_idx,
bool opcodes, bool linum)
{
const struct bpf_insn_cbs cbs = {
.cb_print = print_insn_for_graph,
.cb_call = print_call,
.cb_imm = print_imm,
.private_data = dd,
};
const struct bpf_prog_linfo *prog_linfo = dd->prog_linfo;
const struct bpf_line_info *last_linfo = NULL;
struct bpf_func_info *record = dd->func_info;
struct bpf_insn *insn_start = buf_start;
struct bpf_insn *insn_end = buf_end;
struct bpf_insn *cur = insn_start;
struct btf *btf = dd->btf;
bool double_insn = false;
char func_sig[1024];
for (; cur <= insn_end; cur++) {
unsigned int insn_off;
if (double_insn) {
double_insn = false;
continue;
}
double_insn = cur->code == (BPF_LD | BPF_IMM | BPF_DW);
insn_off = (unsigned int)(cur - insn_start + start_idx);
if (btf && record) {
if (record->insn_off == insn_off) {
btf_dumper_type_only(btf, record->type_id,
func_sig,
sizeof(func_sig));
if (func_sig[0] != '\0')
printf("; %s:\\l\\\n", func_sig);
record = (void *)record + dd->finfo_rec_size;
}
}
if (prog_linfo) {
const struct bpf_line_info *linfo;
linfo = bpf_prog_linfo__lfind(prog_linfo, insn_off, 0);
if (linfo && linfo != last_linfo) {
btf_dump_linfo_dotlabel(btf, linfo, linum);
last_linfo = linfo;
}
}
printf("%u: ", insn_off);
print_bpf_insn(&cbs, cur, true);
if (opcodes) {
printf("\\ \\ \\ \\ ");
fprint_hex(stdout, cur, 8, " ");
if (double_insn && cur <= insn_end - 1) {
printf(" ");
fprint_hex(stdout, cur + 1, 8, " ");
}
printf("\\l\\\n");
}
if (cur != insn_end)
printf("| ");
}
}
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (C) 2018 Netronome Systems, Inc. */
#ifndef __BPF_TOOL_XLATED_DUMPER_H
#define __BPF_TOOL_XLATED_DUMPER_H
#define SYM_MAX_NAME 256
#define MODULE_MAX_NAME 64
struct bpf_prog_linfo;
struct kernel_sym {
unsigned long address;
char name[SYM_MAX_NAME];
char module[MODULE_MAX_NAME];
};
struct dump_data {
unsigned long address_call_base;
struct kernel_sym *sym_mapping;
__u32 sym_count;
__u64 *jited_ksyms;
__u32 nr_jited_ksyms;
struct btf *btf;
void *func_info;
__u32 finfo_rec_size;
const struct bpf_prog_linfo *prog_linfo;
char scratch_buff[SYM_MAX_NAME + 8];
};
void kernel_syms_load(struct dump_data *dd);
void kernel_syms_destroy(struct dump_data *dd);
struct kernel_sym *kernel_syms_search(struct dump_data *dd, unsigned long key);
void dump_xlated_json(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum);
void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
bool opcodes, bool linum);
void dump_xlated_for_graph(struct dump_data *dd, void *buf, void *buf_end,
unsigned int start_index,
bool opcodes, bool linum);
#endif
@@ -0,0 +1,4 @@
/fixdep
/resolve_btfids
/libbpf/
/libsubcmd/
@@ -0,0 +1,12 @@
hostprogs := resolve_btfids
resolve_btfids-y += main.o
resolve_btfids-y += rbtree.o
resolve_btfids-y += zalloc.o
resolve_btfids-y += string.o
resolve_btfids-y += ctype.o
resolve_btfids-y += str_error_r.o
$(OUTPUT)%.o: ../../lib/%.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,host_cc_o_c)
@@ -0,0 +1,119 @@
# SPDX-License-Identifier: GPL-2.0-only
include ../../scripts/Makefile.include
include ../../scripts/Makefile.arch
srctree := $(abspath $(CURDIR)/../../../)
ifeq ($(V),1)
msg =
else
ifeq ($(silent),1)
msg =
else
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
endif
MAKEFLAGS=--no-print-directory
endif
# Overrides for the prepare step libraries.
HOST_OVERRIDES := AR="$(HOSTAR)" CC="$(HOSTCC)" LD="$(HOSTLD)" ARCH="$(HOSTARCH)" \
CROSS_COMPILE="" CLANG_CROSS_FLAGS="" EXTRA_CFLAGS="$(HOSTCFLAGS)"
RM ?= rm
HOSTCC ?= gcc
HOSTLD ?= ld
HOSTAR ?= ar
HOSTPKG_CONFIG ?= pkg-config
CROSS_COMPILE =
OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/
LIBBPF_SRC := $(srctree)/tools/lib/bpf/
SUBCMD_SRC := $(srctree)/tools/lib/subcmd/
BPFOBJ := $(OUTPUT)/libbpf/libbpf.a
LIBBPF_OUT := $(abspath $(dir $(BPFOBJ)))/
SUBCMDOBJ := $(OUTPUT)/libsubcmd/libsubcmd.a
SUBCMD_OUT := $(abspath $(dir $(SUBCMDOBJ)))/
LIBBPF_DESTDIR := $(LIBBPF_OUT)
LIBBPF_INCLUDE := $(LIBBPF_DESTDIR)include
SUBCMD_DESTDIR := $(SUBCMD_OUT)
SUBCMD_INCLUDE := $(SUBCMD_DESTDIR)include
BINARY := $(OUTPUT)/resolve_btfids
BINARY_IN := $(BINARY)-in.o
all: $(BINARY)
prepare: $(BPFOBJ) $(SUBCMDOBJ)
$(OUTPUT) $(OUTPUT)/libsubcmd $(LIBBPF_OUT):
$(call msg,MKDIR,,$@)
$(Q)mkdir -p $(@)
$(SUBCMDOBJ): fixdep FORCE | $(OUTPUT)/libsubcmd
$(Q)$(MAKE) -C $(SUBCMD_SRC) OUTPUT=$(SUBCMD_OUT) \
DESTDIR=$(SUBCMD_DESTDIR) $(HOST_OVERRIDES) prefix= subdir= \
$(abspath $@) install_headers
$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUT)
$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(LIBBPF_OUT) \
DESTDIR=$(LIBBPF_DESTDIR) $(HOST_OVERRIDES) prefix= subdir= \
$(abspath $@) install_headers
LIBELF_FLAGS := $(shell $(HOSTPKG_CONFIG) libelf --cflags 2>/dev/null)
ifneq ($(filter -static,$(EXTRA_LDFLAGS)),)
LIBELF_LIBS := $(shell $(HOSTPKG_CONFIG) libelf --libs --static 2>/dev/null || echo -lelf -lzstd)
else
LIBELF_LIBS := $(shell $(HOSTPKG_CONFIG) libelf --libs 2>/dev/null || echo -lelf)
endif
ZLIB_LIBS := $(shell $(HOSTPKG_CONFIG) zlib --libs 2>/dev/null || echo -lz)
HOSTCFLAGS_resolve_btfids += -g \
-I$(srctree)/tools/include \
-I$(srctree)/tools/include/uapi \
-I$(LIBBPF_INCLUDE) \
-I$(SUBCMD_INCLUDE) \
$(LIBELF_FLAGS) \
-Wall -Werror
LIBS = $(LIBELF_LIBS) $(ZLIB_LIBS)
export srctree OUTPUT HOSTCFLAGS_resolve_btfids Q HOSTCC HOSTLD HOSTAR
include $(srctree)/tools/build/Makefile.include
$(BINARY_IN): fixdep FORCE prepare | $(OUTPUT)
$(Q)$(MAKE) $(build)=resolve_btfids
$(BINARY): $(BPFOBJ) $(SUBCMDOBJ) $(BINARY_IN)
$(call msg,LINK,$@)
$(Q)$(HOSTCC) $(BINARY_IN) $(KBUILD_HOSTLDFLAGS) $(EXTRA_LDFLAGS) -o $@ $(BPFOBJ) $(SUBCMDOBJ) $(LIBS)
clean_objects := $(wildcard $(OUTPUT)/*.o \
$(OUTPUT)/.*.o.cmd \
$(OUTPUT)/.*.o.d \
$(LIBBPF_OUT) \
$(LIBBPF_DESTDIR) \
$(SUBCMD_OUT) \
$(SUBCMD_DESTDIR) \
$(OUTPUT)/resolve_btfids)
ifneq ($(clean_objects),)
clean: fixdep-clean
$(call msg,CLEAN,$(BINARY))
$(Q)$(RM) -rf $(clean_objects)
else
clean:
endif
tags:
$(call msg,GEN,,tags)
$(Q)ctags -R . $(LIBBPF_SRC) $(SUBCMD_SRC)
FORCE:
.PHONY: all FORCE clean tags prepare
File diff suppressed because it is too large Load Diff