bpftool-gen(8) System Manager's Manual bpftool-gen(8)

bpftool-gen - ابزاری برای تولید هدرهای C اسکلت کد BPF

bpftool [OPTIONS] gen COMMAND

OPTIONS := { { -j | --json } [{ -p | --pretty }] | { -d | --debug } | { -L | --use-loader } | [ { -S | --sign } {-k <private_key.pem>} -i <certificate.x509> ] }

COMMAND := { object | skeleton | help }

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

پیوند ایستا (ترکیب) یک یا چند INPUT_FILE در قالب یک پرونده نهایی OUTPUT_FILE واحد. تمام پرونده‌های درگیر، پرونده‌های شیء BPF ELF هستند.

قوانین پیوند ایستای BPF عمدتاً مشابه پرونده‌های شیء فضای کاربری است، اما علاوه بر ترکیب بخش‌های داده و دستورالعمل، داده‌های .BTF و .BTF.ext (در صورت وجود در هر یک از پرونده‌های ورودی) با یکدیگر ترکیب می‌شوند. داده‌های .BTF یکتاسازی (deduplicate) می‌شوند، بنابراین تمام انواع داده مشترک در میان INPUT_FILEها فقط یک بار در اطلاعات نهایی BTF نمایش داده خواهند شد.

پیوند ایستای BPF امکان تقسیم کد منبع BPF را به پرونده‌های کامپایل‌شده جداگانه فراهم می‌کند که سپس در یک پرونده شیء واحد BPF پیوند داده می‌شوند؛ این پرونده می‌تواند برای تولید اسکلت BPF (با دستور gen skeleton) استفاده شود یا مستقیماً به libbpf (با استفاده از خانواده رابط‌های برنامه‌نویسی bpf_object__open()) منتقل گردد.

تولید پرونده هدر C اسکلت BPF برای FILE داده‌شده.

اسکلت BPF یک رابط جایگزین برای رابط‌های برنامه‌نویسی موجود در libbpf به‌منظور کار با اشیاء BPF است. کد اسکلت به‌منظور کوتاه کردن و ساده‌سازی چشمگیر کدهای بارگذاری و کار با برنامه‌های BPF از سمت فضای کاربری در نظر گرفته شده است. کد تولیدشده متناسب با پرونده شیء ورودی BPF (FILE) تنظیم می‌شود و ساختار آن را با فهرست کردن نقشه‌ها، برنامه‌ها، متغیرها و موارد موجود بازتاب می‌دهد. اسکلت نیاز به جستجوی مؤلفه‌های یادشده با نام را از بین می‌برد. در عوض، در صورت موفقیت‌آمیز بودن نمونه‌سازی اسکلت، آن‌ها در ساختار اسکلت به‌صورت انواع معتبر libbpf (مانند اشاره‌گر struct bpf_map) مقداردهی می‌شوند و می‌توانند به رابط‌های برنامه‌نویسی عمومی و موجود libbpf پاس داده شوند.

علاوه بر دسترسی ساده و مطمئن به نقشه‌ها و برنامه‌ها، اسکلت فضایی برای ذخیره‌سازی پیوندهای BPF (struct bpf_link) برای هر برنامه BPF درون شیء BPF فراهم می‌کند. در صورت درخواست، برنامه‌های BPF پشتیبانی‌شده به‌صورت خودکار متصل (attach) می‌شوند و پیوندهای BPF حاصل، برای استفاده‌های بعدی کاربر در فیلدهای از پیش تخصیص‌یافته در ساختار اسکلت ذخیره می‌گردند. برای برنامه‌های BPF که توسط libbpf به‌طور خودکار متصل نمی‌شوند، کاربر می‌تواند آن‌ها را به‌صورت دستی متصل کند، اما پیوند BPF حاصل را در فیلد پیوند مربوط به هر برنامه ذخیره نماید. تمام این پیوندهای برپاشده به‌طور خودکار هنگام نابودی اسکلت BPF از بین می‌روند. این امر نیاز کاربران به مدیریت دستی پیوندها و اتکا به پشتیبانی libbpf برای جداسازی برنامه‌ها و آزادسازی منابع را برطرف می‌کند.

یکی دیگر از قابلیت‌های ارائه‌شده توسط اسکلت BPF، رابطی برای متغیرهای سراسری از تمام انواع پشتیبانی‌شده است: متغیرهای تغییرپذیر، فقط‌خواندنی و همچنین متغیرهای خارجی (extern). این رابط امکان مقداردهی اولیه متغیرها را پیش از بارگذاری و اعتبارسنجی شیء BPF توسط هسته فراهم می‌کند. برای متغیرهای غیر فقط‌خواندنی، می‌توان از همان رابط برای دریافت مقادیر متغیرهای سراسری در سمت فضای کاربری استفاده کرد، حتی اگر توسط کد BPF تغییر یافته باشند.

در حین تولید اسکلت، محتوای پرونده شیء منبع BPF (FILE) درون کد تولیدشده جاسازی می‌شود و بنابراین نیازی به نگهداری پرونده شیء در کنار برنامه نیست. این ویژگی تطابق یک‌به‌یک اسکلت و پرونده شیء BPF و هماهنگ ماندن همیشگی آن‌ها را تضمین می‌کند. کد تولیدشده تحت مجوزهای دوگانه LGPL-2.1 و BSD-2-Clause منتشر می‌شود.

یکی از اهداف طراحی و ضمانت‌های اسکلت این است که رابط‌های آن با رابط‌های برنامه‌نویسی عمومی libbpf تعامل‌پذیر باشند. کاربر همواره باید بتواند از رابط برنامه‌نویسی اسکلت برای ایجاد و بارگذاری شیء BPF استفاده کند و پس از آن از رابط‌های برنامه‌نویسی libbpf برای ادامه کار با نقشه‌ها، برنامه‌ها و موارد مشخص دیگر بهره ببرد.

به‌عنوان بخشی از اسکلت، چند تابع سفارشی تولید می‌شود. نام هر یک از آن‌ها با نام شیء پیشوند می‌خورد. نام شیء می‌تواند از نام پرونده شیء استخراج شود؛ به‌عنوان مثال اگر نام پرونده شیء BPF برابر example.o باشد، نام شیء BPF برابر با example خواهد بود. همچنین نام شیء می‌تواند با پارامتر name OBJECT_NAME به‌طور صریح مشخص گردد. توابع سفارشی زیر فراهم می‌شوند (با فرض اینکه example نام شیء باشد):

  • example__open و example__open_opts. این توابع برای نمونه‌سازی اسکلت استفاده می‌شوند. این مورد با رابط برنامه‌نویسی bpf_object__open() در libbpf مطابقت دارد. گونه _opts گزینه‌های اضافی bpf_object_open_opts را می‌پذیرد.
  • example__load. این تابع نقشه‌ها را ایجاد می‌کند، برنامه‌های BPF را بارگذاری و اعتبارسنجی می‌نماید و نقشه‌های داده‌های سراسری را مقداردهی اولیه می‌کند. این مورد با رابط برنامه‌نویسی bpf_object__load() در libbpf مطابقت دارد.
  • example__open_and_load فراخوانی‌های example__open و example__load را در قالب یک عملیات متداول ترکیب می‌کند.
  • example__attach و example__detach. این جفت تابع به ترتیب امکان متصل کردن (attach) و جدا کردن (detach) شیء BPF از قبل بارگذاری‌شده را فراهم می‌کنند. فقط برنامه‌های BPF از انواع پشتیبانی‌شده توسط libbpf برای اتصال خودکار، به‌صورت خودکار متصل شده و پیوندهای BPF متناظر آن‌ها نمونه‌سازی می‌شوند. برای سایر برنامه‌های BPF، کاربر می‌تواند به‌صورت دستی یک پیوند BPF ایجاد کند و آن را به فیلدهای متناظر در ساختار اسکلت اختصاص دهد. تابع example__detach هم پیوندهای ایجادشده به‌صورت خودکار و هم پیوندهایی که کاربر دستی مقداردهی کرده است را جدا می‌سازد.
  • example__destroy. جداسازی و تخلیه برنامه‌های BPF و آزادسازی تمام منابع استفاده‌شده توسط اسکلت و شیء BPF.

اگر شیء BPF دارای متغیرهای سراسری باشد، ساختارهای متناظری با چینش حافظه منطبق بر چینش بخش داده‌های سراسری ایجاد خواهد شد. بخش‌های داده/ساختارهای پشتیبانی‌شده در حال حاضر عبارتند از: ساختارها/بخش‌های داده .data، .bss، .rodata و .kconfig. این بخش‌های داده/ساختارها در صورتی که پیش از example__load تنظیم شوند، می‌توانند برای مقداردهی اولیه مقادیر متغیرها استفاده شوند. پس از آن، در صورتی که هسته مقصد از آرایه‌های نگاشت‌شده در حافظه BPF پشتیبانی کند، می‌توان از همان ساختارها برای واکشی و به‌روزرسانی داده‌های (غیر فقط‌خواندنی) از سمت فضای کاربری، با همان سادگی سمت BPF استفاده نمود.

تولید پرونده هدر C زیر‌اسکلت (subskeleton) BPF برای FILE داده‌شده.

زیر‌اسکلت‌ها مشابه اسکلت‌ها هستند، با این تفاوت که مالک نقشه‌ها، برنامه‌ها یا متغیرهای سراسری متناظر نیستند. آن‌ها نیازمند این هستند که پرونده شیء استفاده‌شده برای تولیدشان، از قبل از طریق روش‌های دیگر درون یک bpf_object بارگذاری شده باشد.

این قابلیت زمانی مفید است که یک کتابخانه درون یک برنامه بزرگ‌تر BPF گنجانده شده باشد. یک زیر‌اسکلت برای کتابخانه به تمام اشیاء و متغیرهای سراسری تعریف‌شده در آن دسترسی خواهد داشت، بدون اینکه نیازی به دانستن درباره برنامه بزرگ‌تر داشته باشد.

در نتیجه، تنها دو تابع برای زیر‌اسکلت‌ها تعریف شده است:

  • example__open(bpf_object*). نمونه‌سازی یک زیر‌اسکلت از یک bpf_object که از قبل باز شده (اما لزوماً بارگذاری نشده است).
  • example__destroy(). آزادسازی فضای حافظه اختصاص‌یافته برای زیر‌اسکلت؛ اما این تابع برنامه‌ها یا نقشه‌های BPF را تخلیه نمی‌کند.
تولید یک پرونده کمینه BTF به‌عنوان OUTPUT، مشتق‌شده از پرونده ورودی BTF به نام INPUT، شامل تمام انواع داده مورد نیاز BTF تا بازنشانی‌های (relocations) مربوط به CO-RE برای یک یا چند شیء eBPF داده‌شده برآورده شوند.

هنگامی که هسته‌ها با CONFIG_DEBUG_INFO_BTF کامپایل نشده باشند، libbpf در زمان بارگذاری یک شیء eBPF مجبور است به پرونده‌های خارجی BTF تکیه کند تا بتواند بازنشانی‌های CO-RE را محاسبه کند.

معمولاً یک پرونده خارجی BTF با استفاده از pahole از داده‌های موجود DWARF هسته ساخته می‌شود. این پرونده شامل تمام انواع استفاده‌شده توسط تصویر هسته مربوطه است و به همین دلیل بسیار حجیم است.

قابلیت min_core_btf پرونده‌های کوچک‌تر BTF متناسب با یک یا چند شیء eBPF ایجاد می‌کند تا بتوان آن‌ها را همراه با یک برنامه مبتنی بر eBPF CO-RE توزیع کرد و برنامه را برای نسخه‌های مختلف هسته قابل‌حمل (portable) ساخت.

برای اطلاعات بیشتر در مورد نحوه استفاده، مثال‌های زیر را بررسی کنید.

چاپ پیام راهنمای کوتاه.

چاپ پیام راهنمای کوتاه (مشابه bpftool help).
چاپ شماره نسخه bpftool (مشابه bpftool version)، شماره نسخه libbpf مورد استفاده، و ویژگی‌های اختیاری که هنگام کامپایل bpftool گنجانده شده‌اند. ویژگی‌های اختیاری شامل پیوند به LLVM یا libbfd برای فراهم کردن دیس‌اسمبلر برنامه‌های JIT‌شده (bpftool prog dump jited) و استفاده از اسکلت‌های BPF است (برخی ویژگی‌ها مانند bpftool prog profile یا نمایش pidهای مرتبط با اشیاء BPF ممکن است به آن وابسته باشند).
تولید خروجی در قالب JSON. برای دستوراتی که نمی‌توانند خروجی JSON تولید کنند، این گزینه بی‌تأثیر است.
تولید خروجی JSON خوانا برای انسان. متضمن گزینه -j است.
چاپ تمام گزارش‌ها و لاگ‌های در دسترس، حتی اطلاعات در سطح اشکال‌زدایی (debug). این شامل لاگ‌های libbpf و همچنین اعتبارسنج (verifier) در زمان تلاش برای بارگذاری برنامه‌ها است.
برای اسکلت‌ها، یک اسکلت «سبک» (light) تولید می‌کند (که با عنوان اسکلت «loader» نیز شناخته می‌شود). یک اسکلت سبک حاوی یک برنامه eBPF بارگذار (loader) است. این اسکلت از بخش عمده زیرساخت‌های libbpf استفاده نمی‌کند و نیازی به libelf ندارد.
برای اسکلت‌ها، یک اسکلت امضاشده تولید می‌کند. این گزینه باید همراه با -k و -i استفاده شود. استفاده از این فلگ به‌طور ضمنی --use-loader را فعال می‌کند.
مسیر پرونده کلید خصوصی در قالب PEM، مورد نیاز برای امضا کردن.
مسیر پرونده گواهی X.509 در قالب PEM یا DER، مورد نیاز برای امضا کردن.

$ 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,
};

این یک نمونه برنامه BPF با سه برنامه BPF و ترکیبی از نقشه‌ها و متغیرهای سراسری BPF است. کد منبع در سه پرونده کد منبع تقسیم شده است.

$ 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

این مجموعه دستورات، پرونده‌های example1.bpf.c، example2.bpf.c و example3.bpf.c را به‌صورت جداگانه کامپایل کرده و سپس پرونده‌های شیء مربوطه را در قالب پرونده شیء نهایی BPF ELF به نام 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

این یک نسخه خلاصه‌شده از اسکلت تولیدشده برای کد نمونه بالاست.

$ 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>

اکنون پرونده «5.4.0-smaller.btf» می‌تواند توسط libbpf به‌عنوان یک پرونده خارجی BTF هنگام بارگذاری شیء «one.bpf.o» در هسته «5.4.0-example» استفاده شود. توجه داشته باشید که پرونده BTF تولیدشده اجازه بارگذاری سایر اشیاء eBPF را نمی‌دهد، بلکه فقط مواردی که به 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);
...

bpf(2), bpf-helpers(7), bpftool(8), bpftool-btf(8), bpftool-cgroup(8), bpftool-feature(8), bpftool-iter(8), bpftool-link(8), bpftool-map(8), bpftool-net(8), bpftool-perf(8), bpftool-prog(8), bpftool-struct_ops(8), bpftool-token(8)