bpf: btf: Add BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO
This patch adds BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO to support the function debug info. BTF_KIND_FUNC_PROTO must not have a name (i.e. !t->name_off) and it is followed by >= 0 'struct bpf_param' objects to describe the function arguments. The BTF_KIND_FUNC must have a valid name and it must refer back to a BTF_KIND_FUNC_PROTO. The above is the conclusion after the discussion between Edward Cree, Alexei, Daniel, Yonghong and Martin. By combining BTF_KIND_FUNC and BTF_LIND_FUNC_PROTO, a complete function signature can be obtained. It will be used in the later patches to learn the function signature of a running bpf program. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Родитель
b47a0bd23e
Коммит
2667a2626f
|
@ -40,7 +40,8 @@ struct btf_type {
|
|||
/* "size" is used by INT, ENUM, STRUCT and UNION.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
|
||||
* FUNC and FUNC_PROTO.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
|
@ -64,8 +65,10 @@ struct btf_type {
|
|||
#define BTF_KIND_VOLATILE 9 /* Volatile */
|
||||
#define BTF_KIND_CONST 10 /* Const */
|
||||
#define BTF_KIND_RESTRICT 11 /* Restrict */
|
||||
#define BTF_KIND_MAX 11
|
||||
#define NR_BTF_KINDS 12
|
||||
#define BTF_KIND_FUNC 12 /* Function */
|
||||
#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
|
||||
#define BTF_KIND_MAX 13
|
||||
#define NR_BTF_KINDS 14
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
|
@ -110,4 +113,13 @@ struct btf_member {
|
|||
__u32 offset; /* offset in bits */
|
||||
};
|
||||
|
||||
/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
|
||||
* The exact number of btf_param is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_param {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BTF_H__ */
|
||||
|
|
389
kernel/bpf/btf.c
389
kernel/bpf/btf.c
|
@ -5,6 +5,7 @@
|
|||
#include <uapi/linux/types.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
|
@ -259,6 +260,8 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
|
|||
[BTF_KIND_VOLATILE] = "VOLATILE",
|
||||
[BTF_KIND_CONST] = "CONST",
|
||||
[BTF_KIND_RESTRICT] = "RESTRICT",
|
||||
[BTF_KIND_FUNC] = "FUNC",
|
||||
[BTF_KIND_FUNC_PROTO] = "FUNC_PROTO",
|
||||
};
|
||||
|
||||
struct btf_kind_operations {
|
||||
|
@ -281,6 +284,9 @@ struct btf_kind_operations {
|
|||
static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS];
|
||||
static struct btf_type btf_void;
|
||||
|
||||
static int btf_resolve(struct btf_verifier_env *env,
|
||||
const struct btf_type *t, u32 type_id);
|
||||
|
||||
static bool btf_type_is_modifier(const struct btf_type *t)
|
||||
{
|
||||
/* Some of them is not strictly a C modifier
|
||||
|
@ -314,9 +320,20 @@ static bool btf_type_is_fwd(const struct btf_type *t)
|
|||
return BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
|
||||
}
|
||||
|
||||
static bool btf_type_is_func(const struct btf_type *t)
|
||||
{
|
||||
return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC;
|
||||
}
|
||||
|
||||
static bool btf_type_is_func_proto(const struct btf_type *t)
|
||||
{
|
||||
return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC_PROTO;
|
||||
}
|
||||
|
||||
static bool btf_type_nosize(const struct btf_type *t)
|
||||
{
|
||||
return btf_type_is_void(t) || btf_type_is_fwd(t);
|
||||
return btf_type_is_void(t) || btf_type_is_fwd(t) ||
|
||||
btf_type_is_func(t) || btf_type_is_func_proto(t);
|
||||
}
|
||||
|
||||
static bool btf_type_nosize_or_null(const struct btf_type *t)
|
||||
|
@ -433,6 +450,30 @@ static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
|
|||
offset < btf->hdr.str_len;
|
||||
}
|
||||
|
||||
/* Only C-style identifier is permitted. This can be relaxed if
|
||||
* necessary.
|
||||
*/
|
||||
static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)
|
||||
{
|
||||
/* offset must be valid */
|
||||
const char *src = &btf->strings[offset];
|
||||
const char *src_limit;
|
||||
|
||||
if (!isalpha(*src) && *src != '_')
|
||||
return false;
|
||||
|
||||
/* set a limit on identifier length */
|
||||
src_limit = src + KSYM_NAME_LEN;
|
||||
src++;
|
||||
while (*src && src < src_limit) {
|
||||
if (!isalnum(*src) && *src != '_')
|
||||
return false;
|
||||
src++;
|
||||
}
|
||||
|
||||
return !*src;
|
||||
}
|
||||
|
||||
static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
|
||||
{
|
||||
if (!offset)
|
||||
|
@ -747,11 +788,15 @@ static bool env_type_is_resolve_sink(const struct btf_verifier_env *env,
|
|||
/* int, enum or void is a sink */
|
||||
return !btf_type_needs_resolve(next_type);
|
||||
case RESOLVE_PTR:
|
||||
/* int, enum, void, struct or array is a sink for ptr */
|
||||
/* int, enum, void, struct, array, func or func_proto is a sink
|
||||
* for ptr
|
||||
*/
|
||||
return !btf_type_is_modifier(next_type) &&
|
||||
!btf_type_is_ptr(next_type);
|
||||
case RESOLVE_STRUCT_OR_ARRAY:
|
||||
/* int, enum, void or ptr is a sink for struct and array */
|
||||
/* int, enum, void, ptr, func or func_proto is a sink
|
||||
* for struct and array
|
||||
*/
|
||||
return !btf_type_is_modifier(next_type) &&
|
||||
!btf_type_is_array(next_type) &&
|
||||
!btf_type_is_struct(next_type);
|
||||
|
@ -1170,10 +1215,6 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* "typedef void new_void", "const void"...etc */
|
||||
if (btf_type_is_void(next_type) || btf_type_is_fwd(next_type))
|
||||
goto resolved;
|
||||
|
||||
if (!env_type_is_resolve_sink(env, next_type) &&
|
||||
!env_type_is_resolved(env, next_type_id))
|
||||
return env_stack_push(env, next_type, next_type_id);
|
||||
|
@ -1184,13 +1225,18 @@ static int btf_modifier_resolve(struct btf_verifier_env *env,
|
|||
* save us a few type-following when we use it later (e.g. in
|
||||
* pretty print).
|
||||
*/
|
||||
if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
|
||||
!btf_type_nosize(btf_type_id_resolve(btf, &next_type_id))) {
|
||||
btf_verifier_log_type(env, v->t, "Invalid type_id");
|
||||
return -EINVAL;
|
||||
if (!btf_type_id_size(btf, &next_type_id, &next_type_size)) {
|
||||
if (env_type_is_resolved(env, next_type_id))
|
||||
next_type = btf_type_id_resolve(btf, &next_type_id);
|
||||
|
||||
/* "typedef void new_void", "const void"...etc */
|
||||
if (!btf_type_is_void(next_type) &&
|
||||
!btf_type_is_fwd(next_type)) {
|
||||
btf_verifier_log_type(env, v->t, "Invalid type_id");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
resolved:
|
||||
env_stack_pop_resolved(env, next_type_id, next_type_size);
|
||||
|
||||
return 0;
|
||||
|
@ -1203,7 +1249,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
|
|||
const struct btf_type *t = v->t;
|
||||
u32 next_type_id = t->type;
|
||||
struct btf *btf = env->btf;
|
||||
u32 next_type_size = 0;
|
||||
|
||||
next_type = btf_type_by_id(btf, next_type_id);
|
||||
if (!next_type) {
|
||||
|
@ -1211,10 +1256,6 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* "void *" */
|
||||
if (btf_type_is_void(next_type) || btf_type_is_fwd(next_type))
|
||||
goto resolved;
|
||||
|
||||
if (!env_type_is_resolve_sink(env, next_type) &&
|
||||
!env_type_is_resolved(env, next_type_id))
|
||||
return env_stack_push(env, next_type, next_type_id);
|
||||
|
@ -1241,13 +1282,18 @@ static int btf_ptr_resolve(struct btf_verifier_env *env,
|
|||
resolved_type_id);
|
||||
}
|
||||
|
||||
if (!btf_type_id_size(btf, &next_type_id, &next_type_size) &&
|
||||
!btf_type_nosize(btf_type_id_resolve(btf, &next_type_id))) {
|
||||
btf_verifier_log_type(env, v->t, "Invalid type_id");
|
||||
return -EINVAL;
|
||||
if (!btf_type_id_size(btf, &next_type_id, NULL)) {
|
||||
if (env_type_is_resolved(env, next_type_id))
|
||||
next_type = btf_type_id_resolve(btf, &next_type_id);
|
||||
|
||||
if (!btf_type_is_void(next_type) &&
|
||||
!btf_type_is_fwd(next_type) &&
|
||||
!btf_type_is_func_proto(next_type)) {
|
||||
btf_verifier_log_type(env, v->t, "Invalid type_id");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
resolved:
|
||||
env_stack_pop_resolved(env, next_type_id, 0);
|
||||
|
||||
return 0;
|
||||
|
@ -1787,6 +1833,232 @@ static struct btf_kind_operations enum_ops = {
|
|||
.seq_show = btf_enum_seq_show,
|
||||
};
|
||||
|
||||
static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
|
||||
const struct btf_type *t,
|
||||
u32 meta_left)
|
||||
{
|
||||
u32 meta_needed = btf_type_vlen(t) * sizeof(struct btf_param);
|
||||
|
||||
if (meta_left < meta_needed) {
|
||||
btf_verifier_log_basic(env, t,
|
||||
"meta_left:%u meta_needed:%u",
|
||||
meta_left, meta_needed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (t->name_off) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf_verifier_log_type(env, t, NULL);
|
||||
|
||||
return meta_needed;
|
||||
}
|
||||
|
||||
static void btf_func_proto_log(struct btf_verifier_env *env,
|
||||
const struct btf_type *t)
|
||||
{
|
||||
const struct btf_param *args = (const struct btf_param *)(t + 1);
|
||||
u16 nr_args = btf_type_vlen(t), i;
|
||||
|
||||
btf_verifier_log(env, "return=%u args=(", t->type);
|
||||
if (!nr_args) {
|
||||
btf_verifier_log(env, "void");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (nr_args == 1 && !args[0].type) {
|
||||
/* Only one vararg */
|
||||
btf_verifier_log(env, "vararg");
|
||||
goto done;
|
||||
}
|
||||
|
||||
btf_verifier_log(env, "%u %s", args[0].type,
|
||||
btf_name_by_offset(env->btf,
|
||||
args[0].name_off));
|
||||
for (i = 1; i < nr_args - 1; i++)
|
||||
btf_verifier_log(env, ", %u %s", args[i].type,
|
||||
btf_name_by_offset(env->btf,
|
||||
args[i].name_off));
|
||||
|
||||
if (nr_args > 1) {
|
||||
const struct btf_param *last_arg = &args[nr_args - 1];
|
||||
|
||||
if (last_arg->type)
|
||||
btf_verifier_log(env, ", %u %s", last_arg->type,
|
||||
btf_name_by_offset(env->btf,
|
||||
last_arg->name_off));
|
||||
else
|
||||
btf_verifier_log(env, ", vararg");
|
||||
}
|
||||
|
||||
done:
|
||||
btf_verifier_log(env, ")");
|
||||
}
|
||||
|
||||
static struct btf_kind_operations func_proto_ops = {
|
||||
.check_meta = btf_func_proto_check_meta,
|
||||
.resolve = btf_df_resolve,
|
||||
/*
|
||||
* BTF_KIND_FUNC_PROTO cannot be directly referred by
|
||||
* a struct's member.
|
||||
*
|
||||
* It should be a funciton pointer instead.
|
||||
* (i.e. struct's member -> BTF_KIND_PTR -> BTF_KIND_FUNC_PROTO)
|
||||
*
|
||||
* Hence, there is no btf_func_check_member().
|
||||
*/
|
||||
.check_member = btf_df_check_member,
|
||||
.log_details = btf_func_proto_log,
|
||||
.seq_show = btf_df_seq_show,
|
||||
};
|
||||
|
||||
static s32 btf_func_check_meta(struct btf_verifier_env *env,
|
||||
const struct btf_type *t,
|
||||
u32 meta_left)
|
||||
{
|
||||
if (!t->name_off ||
|
||||
!btf_name_valid_identifier(env->btf, t->name_off)) {
|
||||
btf_verifier_log_type(env, t, "Invalid name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (btf_type_vlen(t)) {
|
||||
btf_verifier_log_type(env, t, "vlen != 0");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf_verifier_log_type(env, t, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct btf_kind_operations func_ops = {
|
||||
.check_meta = btf_func_check_meta,
|
||||
.resolve = btf_df_resolve,
|
||||
.check_member = btf_df_check_member,
|
||||
.log_details = btf_ref_type_log,
|
||||
.seq_show = btf_df_seq_show,
|
||||
};
|
||||
|
||||
static int btf_func_proto_check(struct btf_verifier_env *env,
|
||||
const struct btf_type *t)
|
||||
{
|
||||
const struct btf_type *ret_type;
|
||||
const struct btf_param *args;
|
||||
const struct btf *btf;
|
||||
u16 nr_args, i;
|
||||
int err;
|
||||
|
||||
btf = env->btf;
|
||||
args = (const struct btf_param *)(t + 1);
|
||||
nr_args = btf_type_vlen(t);
|
||||
|
||||
/* Check func return type which could be "void" (t->type == 0) */
|
||||
if (t->type) {
|
||||
u32 ret_type_id = t->type;
|
||||
|
||||
ret_type = btf_type_by_id(btf, ret_type_id);
|
||||
if (!ret_type) {
|
||||
btf_verifier_log_type(env, t, "Invalid return type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (btf_type_needs_resolve(ret_type) &&
|
||||
!env_type_is_resolved(env, ret_type_id)) {
|
||||
err = btf_resolve(env, ret_type, ret_type_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Ensure the return type is a type that has a size */
|
||||
if (!btf_type_id_size(btf, &ret_type_id, NULL)) {
|
||||
btf_verifier_log_type(env, t, "Invalid return type");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nr_args)
|
||||
return 0;
|
||||
|
||||
/* Last func arg type_id could be 0 if it is a vararg */
|
||||
if (!args[nr_args - 1].type) {
|
||||
if (args[nr_args - 1].name_off) {
|
||||
btf_verifier_log_type(env, t, "Invalid arg#%u",
|
||||
nr_args);
|
||||
return -EINVAL;
|
||||
}
|
||||
nr_args--;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
for (i = 0; i < nr_args; i++) {
|
||||
const struct btf_type *arg_type;
|
||||
u32 arg_type_id;
|
||||
|
||||
arg_type_id = args[i].type;
|
||||
arg_type = btf_type_by_id(btf, arg_type_id);
|
||||
if (!arg_type) {
|
||||
btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (args[i].name_off &&
|
||||
(!btf_name_offset_valid(btf, args[i].name_off) ||
|
||||
!btf_name_valid_identifier(btf, args[i].name_off))) {
|
||||
btf_verifier_log_type(env, t,
|
||||
"Invalid arg#%u", i + 1);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (btf_type_needs_resolve(arg_type) &&
|
||||
!env_type_is_resolved(env, arg_type_id)) {
|
||||
err = btf_resolve(env, arg_type, arg_type_id);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!btf_type_id_size(btf, &arg_type_id, NULL)) {
|
||||
btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btf_func_check(struct btf_verifier_env *env,
|
||||
const struct btf_type *t)
|
||||
{
|
||||
const struct btf_type *proto_type;
|
||||
const struct btf_param *args;
|
||||
const struct btf *btf;
|
||||
u16 nr_args, i;
|
||||
|
||||
btf = env->btf;
|
||||
proto_type = btf_type_by_id(btf, t->type);
|
||||
|
||||
if (!proto_type || !btf_type_is_func_proto(proto_type)) {
|
||||
btf_verifier_log_type(env, t, "Invalid type_id");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
args = (const struct btf_param *)(proto_type + 1);
|
||||
nr_args = btf_type_vlen(proto_type);
|
||||
for (i = 0; i < nr_args; i++) {
|
||||
if (!args[i].name_off && args[i].type) {
|
||||
btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
|
||||
[BTF_KIND_INT] = &int_ops,
|
||||
[BTF_KIND_PTR] = &ptr_ops,
|
||||
|
@ -1799,6 +2071,8 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
|
|||
[BTF_KIND_VOLATILE] = &modifier_ops,
|
||||
[BTF_KIND_CONST] = &modifier_ops,
|
||||
[BTF_KIND_RESTRICT] = &modifier_ops,
|
||||
[BTF_KIND_FUNC] = &func_ops,
|
||||
[BTF_KIND_FUNC_PROTO] = &func_proto_ops,
|
||||
};
|
||||
|
||||
static s32 btf_check_meta(struct btf_verifier_env *env,
|
||||
|
@ -1870,30 +2144,6 @@ static int btf_check_all_metas(struct btf_verifier_env *env)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btf_resolve(struct btf_verifier_env *env,
|
||||
const struct btf_type *t, u32 type_id)
|
||||
{
|
||||
const struct resolve_vertex *v;
|
||||
int err = 0;
|
||||
|
||||
env->resolve_mode = RESOLVE_TBD;
|
||||
env_stack_push(env, t, type_id);
|
||||
while (!err && (v = env_stack_peak(env))) {
|
||||
env->log_type_id = v->type_id;
|
||||
err = btf_type_ops(v->t)->resolve(env, v);
|
||||
}
|
||||
|
||||
env->log_type_id = type_id;
|
||||
if (err == -E2BIG)
|
||||
btf_verifier_log_type(env, t,
|
||||
"Exceeded max resolving depth:%u",
|
||||
MAX_RESOLVE_DEPTH);
|
||||
else if (err == -EEXIST)
|
||||
btf_verifier_log_type(env, t, "Loop detected");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool btf_resolve_valid(struct btf_verifier_env *env,
|
||||
const struct btf_type *t,
|
||||
u32 type_id)
|
||||
|
@ -1927,6 +2177,39 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
|
|||
return false;
|
||||
}
|
||||
|
||||
static int btf_resolve(struct btf_verifier_env *env,
|
||||
const struct btf_type *t, u32 type_id)
|
||||
{
|
||||
u32 save_log_type_id = env->log_type_id;
|
||||
const struct resolve_vertex *v;
|
||||
int err = 0;
|
||||
|
||||
env->resolve_mode = RESOLVE_TBD;
|
||||
env_stack_push(env, t, type_id);
|
||||
while (!err && (v = env_stack_peak(env))) {
|
||||
env->log_type_id = v->type_id;
|
||||
err = btf_type_ops(v->t)->resolve(env, v);
|
||||
}
|
||||
|
||||
env->log_type_id = type_id;
|
||||
if (err == -E2BIG) {
|
||||
btf_verifier_log_type(env, t,
|
||||
"Exceeded max resolving depth:%u",
|
||||
MAX_RESOLVE_DEPTH);
|
||||
} else if (err == -EEXIST) {
|
||||
btf_verifier_log_type(env, t, "Loop detected");
|
||||
}
|
||||
|
||||
/* Final sanity check */
|
||||
if (!err && !btf_resolve_valid(env, t, type_id)) {
|
||||
btf_verifier_log_type(env, t, "Invalid resolve state");
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
env->log_type_id = save_log_type_id;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btf_check_all_types(struct btf_verifier_env *env)
|
||||
{
|
||||
struct btf *btf = env->btf;
|
||||
|
@ -1949,10 +2232,16 @@ static int btf_check_all_types(struct btf_verifier_env *env)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (btf_type_needs_resolve(t) &&
|
||||
!btf_resolve_valid(env, t, type_id)) {
|
||||
btf_verifier_log_type(env, t, "Invalid resolve state");
|
||||
return -EINVAL;
|
||||
if (btf_type_is_func_proto(t)) {
|
||||
err = btf_func_proto_check(env, t);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (btf_type_is_func(t)) {
|
||||
err = btf_func_check(env, t);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче