Officeyutong / c-struct-bindgen

Generate C structs serialization-free bindings and marshal / unmarshal functions for JSON (Can be used for pass structs between Wasm and eBPF/host)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

struct-bindgen: serialization-free passing of C structs between host/eBPF and Wasm runtime

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

Usage - Genereate Struct to Struct binding

See examples/ for examples.

  1. Create a C header to define a C 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.

  2. 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

Usage - Genereate Struct to Json binding

See examples/ for examples.

  1. Create a C header to define a C 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.

  2. 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

Usage - Genereate Struct to Struct marshal function

See examples/ for examples.

  1. Create a C header to define a C 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.

  2. 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

Usage - From pre-compiled bpf object with BTF info

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 Pre-build Release

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:

build

This tool relies on libbpf.

Install Dependencies

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

build executable

make

The binary can be found in

Roadmap

  • 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

About

Generate C structs serialization-free bindings and marshal / unmarshal functions for JSON (Can be used for pass structs between Wasm and eBPF/host)

License:MIT License


Languages

Language:C++ 89.2%Language:C 8.4%Language:CMake 2.1%Language:Makefile 0.3%