Skip to content

Commit abd0931

Browse files
anakryikoNobody
authored andcommitted
libbpf: support custom SEC() handlers
Allow registering and unregistering custom handlers for BPF program. This allows user applications and libraries to plug into libbpf's declarative SEC() definition handling logic. This allows to offload complex and intricate custom logic into external libraries, but still provide a great user experience. One such example is USDT handling library, which has a lot of code and complexity which doesn't make sense to put into libbpf directly, but it would be really great for users to be able to specify BPF programs with something like SEC("usdt/<path-to-binary>:<usdt_provider>:<usdt_name>") and have correct BPF program type set (BPF_PROGRAM_TYPE_KPROBE, as it is uprobe) and even support BPF skeleton's auto-attach logic. In some cases, it might be even good idea to override libbpf's default handling, like for SEC("perf_event") programs. With custom library, it's possible to extend logic to support specifying perf event specification right there in SEC() definition without burdening libbpf with lots of custom logic or extra library dependecies (e.g., libpfm4). With current patch it's possible to override libbpf's SEC("perf_event") handling and specify a completely custom ones. Further, it's possible to specify a generic fallback handling for any SEC() that doesn't match any other custom or standard libbpf handlers. This allows to accommodate whatever legacy use cases there might be, if necessary. See doc comments for libbpf_register_prog_handler() and libbpf_unregister_prog_handler() for detailed semantics. This patch also bumps libbpf development version to v0.8 and adds new APIs there. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Reviewed-by: Alan Maguire <alan.maguire@oracle.com> Tested-by: Alan Maguire <alan.maguire@oracle.com>
1 parent c31dbcd commit abd0931

File tree

4 files changed

+246
-53
lines changed

4 files changed

+246
-53
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 152 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,6 @@ struct reloc_desc {
201201
};
202202
};
203203

204-
typedef int (*libbpf_prog_setup_fn_t)(struct bpf_program *prog, long cookie);
205-
typedef int (*libbpf_prog_prepare_load_fn_t)(struct bpf_program *prog,
206-
struct bpf_prog_load_opts *opts, long cookie);
207-
/* If auto-attach is not supported, callback should return 0 and set link to NULL */
208-
typedef int (*libbpf_prog_attach_fn_t)(const struct bpf_program *prog, long cookie,
209-
struct bpf_link **link);
210-
211204
/* stored as sec_def->cookie for all libbpf-supported SEC()s */
212205
enum sec_def_flags {
213206
SEC_NONE = 0,
@@ -235,10 +228,11 @@ enum sec_def_flags {
235228
};
236229

237230
struct bpf_sec_def {
238-
const char *sec;
231+
char *sec;
239232
enum bpf_prog_type prog_type;
240233
enum bpf_attach_type expected_attach_type;
241234
long cookie;
235+
int handler_id;
242236

243237
libbpf_prog_setup_fn_t prog_setup_fn;
244238
libbpf_prog_prepare_load_fn_t prog_prepare_load_fn;
@@ -8590,7 +8584,7 @@ int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log
85908584
}
85918585

85928586
#define SEC_DEF(sec_pfx, ptype, atype, flags, ...) { \
8593-
.sec = sec_pfx, \
8587+
.sec = (char *)sec_pfx, \
85948588
.prog_type = BPF_PROG_TYPE_##ptype, \
85958589
.expected_attach_type = atype, \
85968590
.cookie = (long)(flags), \
@@ -8683,61 +8677,167 @@ static const struct bpf_sec_def section_defs[] = {
86838677
SEC_DEF("sk_lookup", SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
86848678
};
86858679

8686-
#define MAX_TYPE_NAME_SIZE 32
8680+
static size_t custom_sec_def_cnt;
8681+
static struct bpf_sec_def *custom_sec_defs;
8682+
static struct bpf_sec_def custom_fallback_def;
8683+
static bool has_custom_fallback_def;
86878684

8688-
static const struct bpf_sec_def *find_sec_def(const char *sec_name)
8685+
static int last_custom_sec_def_handler_id;
8686+
8687+
int libbpf_register_prog_handler(const char *sec,
8688+
enum bpf_prog_type prog_type,
8689+
enum bpf_attach_type exp_attach_type,
8690+
const struct libbpf_prog_handler_opts *opts)
86898691
{
8690-
const struct bpf_sec_def *sec_def;
8691-
enum sec_def_flags sec_flags;
8692-
int i, n = ARRAY_SIZE(section_defs), len;
8693-
bool strict = libbpf_mode & LIBBPF_STRICT_SEC_NAME;
8692+
struct bpf_sec_def *sec_def;
86948693

8695-
for (i = 0; i < n; i++) {
8696-
sec_def = &section_defs[i];
8697-
sec_flags = sec_def->cookie;
8698-
len = strlen(sec_def->sec);
8694+
if (!OPTS_VALID(opts, libbpf_prog_handler_opts))
8695+
return libbpf_err(-EINVAL);
86998696

8700-
/* "type/" always has to have proper SEC("type/extras") form */
8701-
if (sec_def->sec[len - 1] == '/') {
8702-
if (str_has_pfx(sec_name, sec_def->sec))
8703-
return sec_def;
8704-
continue;
8705-
}
8697+
if (last_custom_sec_def_handler_id == INT_MAX) /* prevent overflow */
8698+
return libbpf_err(-E2BIG);
87068699

8707-
/* "type+" means it can be either exact SEC("type") or
8708-
* well-formed SEC("type/extras") with proper '/' separator
8709-
*/
8710-
if (sec_def->sec[len - 1] == '+') {
8711-
len--;
8712-
/* not even a prefix */
8713-
if (strncmp(sec_name, sec_def->sec, len) != 0)
8714-
continue;
8715-
/* exact match or has '/' separator */
8716-
if (sec_name[len] == '\0' || sec_name[len] == '/')
8717-
return sec_def;
8718-
continue;
8719-
}
8700+
if (sec) {
8701+
sec_def = libbpf_reallocarray(custom_sec_defs, custom_sec_def_cnt + 1,
8702+
sizeof(*sec_def));
8703+
if (!sec_def)
8704+
return libbpf_err(-ENOMEM);
87208705

8721-
/* SEC_SLOPPY_PFX definitions are allowed to be just prefix
8722-
* matches, unless strict section name mode
8723-
* (LIBBPF_STRICT_SEC_NAME) is enabled, in which case the
8724-
* match has to be exact.
8725-
*/
8726-
if ((sec_flags & SEC_SLOPPY_PFX) && !strict) {
8727-
if (str_has_pfx(sec_name, sec_def->sec))
8728-
return sec_def;
8729-
continue;
8730-
}
8706+
custom_sec_defs = sec_def;
8707+
sec_def = &custom_sec_defs[custom_sec_def_cnt];
8708+
} else {
8709+
if (has_custom_fallback_def)
8710+
return libbpf_err(-EBUSY);
87318711

8732-
/* Definitions not marked SEC_SLOPPY_PFX (e.g.,
8733-
* SEC("syscall")) are exact matches in both modes.
8734-
*/
8735-
if (strcmp(sec_name, sec_def->sec) == 0)
8712+
sec_def = &custom_fallback_def;
8713+
}
8714+
8715+
sec_def->sec = sec ? strdup(sec) : NULL;
8716+
if (sec && !sec_def->sec)
8717+
return libbpf_err(-ENOMEM);
8718+
8719+
sec_def->prog_type = prog_type;
8720+
sec_def->expected_attach_type = exp_attach_type;
8721+
sec_def->cookie = OPTS_GET(opts, cookie, 0);
8722+
8723+
sec_def->prog_setup_fn = OPTS_GET(opts, prog_setup_fn, NULL);
8724+
sec_def->prog_prepare_load_fn = OPTS_GET(opts, prog_prepare_load_fn, NULL);
8725+
sec_def->prog_attach_fn = OPTS_GET(opts, prog_attach_fn, NULL);
8726+
8727+
sec_def->handler_id = ++last_custom_sec_def_handler_id;
8728+
8729+
if (sec)
8730+
custom_sec_def_cnt++;
8731+
else
8732+
has_custom_fallback_def = true;
8733+
8734+
return sec_def->handler_id;
8735+
}
8736+
8737+
int libbpf_unregister_prog_handler(int handler_id)
8738+
{
8739+
struct bpf_sec_def *sec_defs;
8740+
int i;
8741+
8742+
if (handler_id <= 0)
8743+
return libbpf_err(-EINVAL);
8744+
8745+
if (has_custom_fallback_def && custom_fallback_def.handler_id == handler_id) {
8746+
memset(&custom_fallback_def, 0, sizeof(custom_fallback_def));
8747+
has_custom_fallback_def = false;
8748+
return 0;
8749+
}
8750+
8751+
for (i = 0; i < custom_sec_def_cnt; i++) {
8752+
if (custom_sec_defs[i].handler_id == handler_id)
8753+
break;
8754+
}
8755+
8756+
if (i == custom_sec_def_cnt)
8757+
return libbpf_err(-ENOENT);
8758+
8759+
free(custom_sec_defs[i].sec);
8760+
for (i = i + 1; i < custom_sec_def_cnt; i++)
8761+
custom_sec_defs[i - 1] = custom_sec_defs[i];
8762+
custom_sec_def_cnt--;
8763+
8764+
/* try to shrink the array, but it's ok if we couldn't */
8765+
sec_defs = libbpf_reallocarray(custom_sec_defs, custom_sec_def_cnt, sizeof(*sec_defs));
8766+
if (sec_defs)
8767+
custom_sec_defs = sec_defs;
8768+
8769+
return 0;
8770+
}
8771+
8772+
static bool sec_def_matches(const struct bpf_sec_def *sec_def, const char *sec_name,
8773+
bool allow_sloppy)
8774+
{
8775+
size_t len = strlen(sec_def->sec);
8776+
8777+
/* "type/" always has to have proper SEC("type/extras") form */
8778+
if (sec_def->sec[len - 1] == '/') {
8779+
if (str_has_pfx(sec_name, sec_def->sec))
8780+
return true;
8781+
return false;
8782+
}
8783+
8784+
/* "type+" means it can be either exact SEC("type") or
8785+
* well-formed SEC("type/extras") with proper '/' separator
8786+
*/
8787+
if (sec_def->sec[len - 1] == '+') {
8788+
len--;
8789+
/* not even a prefix */
8790+
if (strncmp(sec_name, sec_def->sec, len) != 0)
8791+
return false;
8792+
/* exact match or has '/' separator */
8793+
if (sec_name[len] == '\0' || sec_name[len] == '/')
8794+
return true;
8795+
return false;
8796+
}
8797+
8798+
/* SEC_SLOPPY_PFX definitions are allowed to be just prefix
8799+
* matches, unless strict section name mode
8800+
* (LIBBPF_STRICT_SEC_NAME) is enabled, in which case the
8801+
* match has to be exact.
8802+
*/
8803+
if (allow_sloppy && str_has_pfx(sec_name, sec_def->sec))
8804+
return true;
8805+
8806+
/* Definitions not marked SEC_SLOPPY_PFX (e.g.,
8807+
* SEC("syscall")) are exact matches in both modes.
8808+
*/
8809+
return strcmp(sec_name, sec_def->sec) == 0;
8810+
}
8811+
8812+
static const struct bpf_sec_def *find_sec_def(const char *sec_name)
8813+
{
8814+
const struct bpf_sec_def *sec_def;
8815+
int i, n;
8816+
bool strict = libbpf_mode & LIBBPF_STRICT_SEC_NAME, allow_sloppy;
8817+
8818+
n = custom_sec_def_cnt;
8819+
for (i = 0; i < n; i++) {
8820+
sec_def = &custom_sec_defs[i];
8821+
if (sec_def_matches(sec_def, sec_name, false))
8822+
return sec_def;
8823+
}
8824+
8825+
n = ARRAY_SIZE(section_defs);
8826+
for (i = 0; i < n; i++) {
8827+
sec_def = &section_defs[i];
8828+
allow_sloppy = (sec_def->cookie & SEC_SLOPPY_PFX) && !strict;
8829+
if (sec_def_matches(sec_def, sec_name, allow_sloppy))
87368830
return sec_def;
87378831
}
8832+
8833+
if (has_custom_fallback_def)
8834+
return &custom_fallback_def;
8835+
87388836
return NULL;
87398837
}
87408838

8839+
#define MAX_TYPE_NAME_SIZE 32
8840+
87418841
static char *libbpf_get_type_names(bool attach_type)
87428842
{
87438843
int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE;

tools/lib/bpf/libbpf.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,93 @@ LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
13281328
LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker);
13291329
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
13301330

1331+
/*
1332+
* Custom handling of BPF program's SEC() definitions
1333+
*/
1334+
1335+
struct bpf_prog_load_opts; /* defined in bpf.h */
1336+
1337+
/* Called during bpf_object__open() for each recognized BPF program. Callback
1338+
* can use various bpf_program__set_*() setters to adjust whatever properties
1339+
* are necessary.
1340+
*/
1341+
typedef int (*libbpf_prog_setup_fn_t)(struct bpf_program *prog, long cookie);
1342+
1343+
/* Called right before libbpf performs bpf_prog_load() to load BPF program
1344+
* into the kernel. Callback can adjust opts as necessary.
1345+
*/
1346+
typedef int (*libbpf_prog_prepare_load_fn_t)(struct bpf_program *prog,
1347+
struct bpf_prog_load_opts *opts, long cookie);
1348+
1349+
/* Called during skeleton attach or through bpf_program__attach(). If
1350+
* auto-attach is not supported, callback should return 0 and set link to
1351+
* NULL (it's not considered an error during skeleton attach, but it will be
1352+
* an error for bpf_program__attach() calls). On error, error should be
1353+
* returned directly and link set to NULL. On success, return 0 and set link
1354+
* to a valid struct bpf_link.
1355+
*/
1356+
typedef int (*libbpf_prog_attach_fn_t)(const struct bpf_program *prog, long cookie,
1357+
struct bpf_link **link);
1358+
1359+
struct libbpf_prog_handler_opts {
1360+
/* size of this struct, for forward/backward compatiblity */
1361+
size_t sz;
1362+
/* User-provided cookie passed to each callback */
1363+
long cookie;
1364+
/* BPF program initialization callback (see libbpf_prog_setup_fn_t) */
1365+
libbpf_prog_setup_fn_t prog_setup_fn;
1366+
/* BPF program loading callback (see libbpf_prog_prepare_load_fn_t) */
1367+
libbpf_prog_prepare_load_fn_t prog_prepare_load_fn;
1368+
/* BPF program attach callback (see libbpf_prog_attach_fn_t) */
1369+
libbpf_prog_attach_fn_t prog_attach_fn;
1370+
};
1371+
#define libbpf_prog_handler_opts__last_field prog_attach_fn
1372+
1373+
/**
1374+
* @brief **libbpf_register_prog_handler()** registers a custom BPF program
1375+
* SEC() handler.
1376+
* @param sec section prefix for which custom handler is registered
1377+
* @param prog_type BPF program type associated with specified section
1378+
* @param exp_attach_type Expected BPF attach type associated with specified section
1379+
* @param opts optional cookie, callbacks, and other extra options
1380+
* @return Non-negative handler ID is returned on success. This handler ID has
1381+
* to be passed to *libbpf_unregister_prog_handler()* to unregister such
1382+
* custom handler. Negative error code is returned on error.
1383+
*
1384+
* *sec* defines which SEC() definitions are handled by this custom handler
1385+
* registration. *sec* can have few different forms:
1386+
* - if *sec* is just a plain string (e.g., "abc"), it will match only
1387+
* SEC("abc"). If BPF program specifies SEC("abc/whatever") it will result
1388+
* in an error;
1389+
* - if *sec* is of the form "abc/", proper SEC() form is
1390+
* SEC("abc/something"), where acceptable "something" should be checked by
1391+
* *prog_init_fn* callback, if there are additional restrictions;
1392+
* - if *sec* is of the form "abc+", it will successfully match both
1393+
* SEC("abc") and SEC("abc/whatever") forms;
1394+
* - if *sec* is NULL, custom handler is registered for any BPF program that
1395+
* doesn't match any of the registered (custom or libbpf's own) SEC()
1396+
* handlers. There could be only one such generic custom handler registered
1397+
* at any given time.
1398+
*
1399+
* All custom handlers (except the one with *sec* == NULL) are processed
1400+
* before libbpf's own SEC() handlers. It is allowed to "override" libbpf's
1401+
* SEC() handlers by registering custom ones for the same section prefix
1402+
* (i.e., it's possible to have custom SEC("perf_event/LLC-load-misses")
1403+
* handler).
1404+
*/
1405+
LIBBPF_API int libbpf_register_prog_handler(const char *sec,
1406+
enum bpf_prog_type prog_type,
1407+
enum bpf_attach_type exp_attach_type,
1408+
const struct libbpf_prog_handler_opts *opts);
1409+
/**
1410+
* @brief *libbpf_unregister_prog_handler()* unregisters previously registered
1411+
* custom BPF program SEC() handler.
1412+
* @param handler_id handler ID returned by *libbpf_register_prog_handler()*
1413+
* after successful registration
1414+
* @return 0 on success, negative error code if handler isn't found
1415+
*/
1416+
LIBBPF_API int libbpf_unregister_prog_handler(int handler_id);
1417+
13311418
#ifdef __cplusplus
13321419
} /* extern "C" */
13331420
#endif

tools/lib/bpf/libbpf.map

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,9 @@ LIBBPF_0.7.0 {
439439
libbpf_probe_bpf_prog_type;
440440
libbpf_set_memlock_rlim_max;
441441
} LIBBPF_0.6.0;
442+
443+
LIBBPF_0.8.0 {
444+
global:
445+
libbpf_register_prog_handler;
446+
libbpf_unregister_prog_handler;
447+
} LIBBPF_0.7.0;

tools/lib/bpf/libbpf_version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
#define __LIBBPF_VERSION_H
55

66
#define LIBBPF_MAJOR_VERSION 0
7-
#define LIBBPF_MINOR_VERSION 7
7+
#define LIBBPF_MINOR_VERSION 8
88

99
#endif /* __LIBBPF_VERSION_H */

0 commit comments

Comments
 (0)