A tool for generate marshal and unmarshal functions or serialization-free bindings for C structs using BTF info.
With a C struct defined like this:
#ifndef __SIGSNOOP_H
#define __SIGSNOOP_H
#define TASK_COMM_LEN 13
struct event2 {
char b;
float x;
double y;
int z;
long long int a;
short comm[TASK_COMM_LEN];
void* unused_ptr;
};
#endif
This tool can be use to:
-
generate struct bindings for correctly representing C struct layout, which can be used to pass c struct from ebpf programs and host environments, to wasm runtime in eunomia-bpf project, for example:
struct event2 { char b; char __pad0[3]; float x; double y; int z; char __pad1[4]; long long a; short comm[13]; char __pad2[6]; uint64_t unused_ptr; char end; char __pad3[7]; } __attribute__((packed)); static_assert(sizeof(struct event2) == 80, "Size of event2 is not 80");
This can be used for serialization-free passing of C structs between host/eBPF and Wasm runtime. Zero Overhead!
-
generate marshal and unmarshal functions for convert C structs to JSON format, for example:
static char * marshal_struct_output_event__to_json_str(const struct output_event *src) { assert(src); cJSON *object = cJSON_CreateObject(); cJSON *pid_object = cJSON_CreateNumber(src->pid); cJSON_AddItemToObject(object, "pid", object_object); .... return cJSON_PrintUnformatted(object); } static struct output_event * unmarshal_struct_output_event__from_json_str(struct event *dst, const char *src) { assert(dst && src); cJSON *object = cJSON_Parse(src); if (!object) { return NULL; } cJSON *pid_object = cJSON_GetObjectItemCaseSensitive(object, "pid"); dst->pid = pid_object->valueint; .... return dst; }
See examples/
for examples.
-
Create a
C
header to define aC
struct, for example:examples/test-event.h:
#ifndef __SIGSNOOP_H #define __SIGSNOOP_H #define TASK_COMM_LEN 13 struct event2 { char b; float x; double y; int z; long long int a; short comm[TASK_COMM_LEN]; void *unused_ptr; char end; }; #endif /* __SIGSNOOP_H */
The number of structs, names of structs are not limited. Struct fields can be any valid C types, including structs, unions, arrays, pointers, etc.
-
Generate bindings:
ecc examples/test-event.h --header-only struct-bindgen examples/source.bpf.o > source-struct-binding.h
You will get a
source-struct-binding.h
file, for example:// Code generated by c-struct-bindgen and ecc - DO NOT EDIT // See https://github.com/eunomia-bpf/c-struct-bindgen for details. // struct-bindgen versions: 0.1.0 // source file path: /home/yunwei/c-struct-bindgen/examples/test-event.bpf.o #ifndef __STRUCT_MARSHAL_TEST-EVENT_BPF_O_H__ #define __STRUCT_MARSHAL_TEST-EVENT_BPF_O_H__ #include <assert.h> #include <string.h> #include <stdint.h> struct event2 { char b; char __pad0[3]; float x; double y; int z; char __pad1[4]; long long a; short comm[13]; char __pad2[6]; uint64_t unused_ptr; char end; char __pad3[7]; } __attribute__((packed)); static_assert(sizeof(struct event2) == 80, "Size of event2 is not 80"); #endif
See examples/
for examples.
-
Create a
C
header to define aC
struct, for example:examples/test-event.h:
#ifndef __SIGSNOOP_H #define __SIGSNOOP_H struct output_event { unsigned int pid; double time; }; #endif /* __SIGSNOOP_H */
The number of structs, names of structs are not limited. Struct fields can be any valid C types, including structs, unions, arrays, pointers, etc.
-
Generate bindings:
ecc examples/test-event.h --header-only struct-bindgen examples/source.bpf.o -j > source-struct-binding.h
You will get a
source-struct-binding.h
file, for example:// Code generated by c-struct-bindgen and ecc - DO NOT EDIT // See https://github.com/eunomia-bpf/c-struct-bindgen for details. // struct-bindgen versions: 0.1.0 // source file path: /home/yunwei/c-struct-bindgen/examples/test-event.bpf.o #ifndef __STRUCT_MARSHAL_TEST_EVENT_BPF_O_H__ #define __STRUCT_MARSHAL_TEST_EVENT_BPF_O_H__ #include <assert.h> #include <string.h> #include <stdint.h> #include "cJSON.h" static char * marshal_struct_output_event__to_json_str(const struct output_event *src) { assert(src); cJSON *object = cJSON_CreateObject(); if (!object) { return NULL; } cJSON *pid_object = cJSON_CreateNumber(src->pid); if (!pid_object) { cJSON_Delete(object); return NULL; } if (!cJSON_AddItemToObject(object, "pid", object_object)) { cJSON_Delete(object); return NULL; } cJSON *time_object = cJSON_CreateNumber(src->time); if (!time_object) { cJSON_Delete(object); return NULL; } if (!cJSON_AddItemToObject(object, "time", object_object)) { cJSON_Delete(object); return NULL; } return cJSON_PrintUnformatted(object); } static struct output_event * unmarshal_struct_output_event__from_json_str(struct event *dst, const char *src) { assert(dst && src); cJSON *object = cJSON_Parse(src); if (!object) { return NULL; } cJSON *pid_object = cJSON_GetObjectItemCaseSensitive(object, "pid"); if (!cJSON_IsNumber(pid_object)) { cJSON_Delete(object); return NULL; } dst->pid = pid_object->valueint; cJSON *time_object = cJSON_GetObjectItemCaseSensitive(object, "time"); if (!cJSON_IsNumber(time_object)) { cJSON_Delete(object); return NULL; } dst->time = time_object->valuedouble; return dst; } #endif
See examples/
for examples.
-
Create a
C
header to define aC
struct, for example:examples/test-event.h:
#ifndef __SIGSNOOP_H #define __SIGSNOOP_H struct event2 { void* unused_ptr; float x; double y; int z; long long int a; short comm[16]; }; #endif /* __SIGSNOOP_H */
The number of structs, names of structs are not limited. Struct fields can be any valid C types, including structs, unions, arrays, pointers, etc.
-
Generate bindings:
ecc examples/test-event.h --header-only struct-bindgen examples/source.bpf.o --marshal > source-struct-binding.h
You will get a
source-struct-binding.h
file, for example:// Code generated by c-struct-bindgen and ecc - DO NOT EDIT // See https://github.com/eunomia-bpf/c-struct-bindgen for details. // struct-bindgen versions: 0.1.0 // source file path: /home/yunwei/c-struct-bindgen/examples/source.bpf.o #ifndef __STRUCT_MARSHAL_SOURCE_BPF_O_H__ #define __STRUCT_MARSHAL_SOURCE_BPF_O_H__ #include <assert.h> #include <string.h> #include <stdint.h> static void marshal_struct_event__to_binary(void *dst, const struct event *src) { assert(dst && src); *(unsigned long long*)(dst + 0) = src->ts; *(int*)(dst + 8) = src->pid; *(int*)(dst + 12) = src->uid; *(int*)(dst + 16) = src->ret; *(int*)(dst + 20) = src->flags; memcpy(dst + 24, src->comm, 16); } static void unmarshal_struct_event__from_binary(struct event *dst, const void *src) { assert(dst && src); dst->ts = *(unsigned long long*)(src + 0); dst->pid = *(int*)(src + 8); dst->uid = *(int*)(src + 12); dst->ret = *(int*)(src + 16); dst->flags = *(int*)(src + 20); memcpy(dst->comm, src + 24, 16); } #endif
struct-bindgen examples/source.bpf.o
You will get a source-struct-binding.h
file, for correct access to the C struct memory in the bpf programs or host env.
Download ecc:
$ wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc && chmod +x ./ecc
$ ./ecc -h
eunomia-bpf compiler
Usage: ecc [OPTIONS] <SOURCE_PATH> [EXPORT_EVENT_HEADER]
Download struct-bindgen:
This tool relies on libbpf.
This tool relies on libbpf. You will need clang
, libelf
and zlib
to build the examples, package names may vary across distros.
On Ubuntu/Debian, you need:
apt install clang libelf1 libelf-dev zlib1g-dev
On CentOS/Fedora, you need:
dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
make
The binary can be found in
- Support for generate C struct marshal functions in C
- Support for generate C struct map layout in C
- Support for generate C struct marshal functions to JSON
- Support for union in structs fields
- Support for composite types in array fields
- handle byte order in host env
- Support for print out info in JSON format