Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3bfc155
add proposal doc
saxena-anurag Aug 12, 2025
912cc62
update doc
saxena-anurag Aug 12, 2025
b26ae77
update doc
saxena-anurag Aug 12, 2025
9b408d1
cr comments
saxena-anurag Aug 18, 2025
a45ac14
implement extensible maps
saxena-anurag Nov 15, 2025
14bd9bb
patch
saxena-anurag Nov 16, 2025
0cf4579
changes
saxena-anurag Nov 17, 2025
8baea8c
fix build breaks
saxena-anurag Nov 19, 2025
3a76764
add unit tests
saxena-anurag Nov 21, 2025
9211235
add program load tests
saxena-anurag Nov 22, 2025
4a275a9
update tests, export epoch APIs
saxena-anurag Nov 24, 2025
9b92684
fix validation, add driver tests
saxena-anurag Nov 25, 2025
91886cd
add epoch api usage in unit tests
saxena-anurag Nov 25, 2025
6924483
add array map
saxena-anurag Dec 2, 2025
3756d5b
fix
saxena-anurag Dec 3, 2025
5ac337a
add doc
saxena-anurag Dec 3, 2025
c0c7cf8
update sample program, fix analysis failure
saxena-anurag Dec 3, 2025
ea1b288
bugfix, add tests
saxena-anurag Dec 3, 2025
8ac47a5
add tests, fixes
saxena-anurag Dec 4, 2025
ea3afd6
Merge branch 'main' into user/anusa/extensible_maps
saxena-anurag Dec 4, 2025
248683e
fix
saxena-anurag Dec 5, 2025
17fa874
fix build break
saxena-anurag Dec 5, 2025
98e8c73
fixes
saxena-anurag Dec 5, 2025
23786d2
Merge branch 'main' into user/anusa/extensible_maps
saxena-anurag Dec 5, 2025
7a0caa1
cleanup
saxena-anurag Dec 5, 2025
2b09b19
cleanup
saxena-anurag Dec 5, 2025
0c0273e
fix asan build
saxena-anurag Dec 5, 2025
053dba6
add hash map to unit tests
saxena-anurag Dec 7, 2025
5b97b69
add tests
saxena-anurag Dec 8, 2025
01b9fdc
fix tests
saxena-anurag Dec 8, 2025
2396181
test code refactor
saxena-anurag Dec 8, 2025
5e26bdb
add tests
saxena-anurag Dec 9, 2025
a66d544
code cleanup
saxena-anurag Dec 9, 2025
b5d316c
cleanup
saxena-anurag Dec 9, 2025
834ab3d
Merge branch 'main' into user/anusa/extensible_maps
saxena-anurag Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 113 additions & 8 deletions docs/eBpfExtensions.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# eBPF extensions

## 1 Overview
An "eBPF extension" is a Windows kernel driver or component that implements eBPF hooks or helper functions. The design
of eBPF for Windows is such that an extension providing an implementation for hooks and helper functions can be
An "eBPF extension" is a Windows kernel driver or component that implements eBPF hooks, helper functions and extensible maps. The design
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To verify: So an extension can implement an extensible map but it cannot implement a non-extensible map? I'll observe that this document never defines the term "extensible map", so either define it or remove that term entirely. Line 30 says "program type specific map", should this line use that language?

of eBPF for Windows is such that an extension providing an implementation for hooks, helper functions and maps can be
developed and deployed without the need to modify either the eBPF execution context or the eBPF verifier.

## 1.1 Windows Network Module Registrar
Expand All @@ -24,9 +24,10 @@ and the various aspects of developing NMR modules as described in
[NMR documentation](https://docs.microsoft.com/en-us/windows-hardware/drivers/network/network-module-registrar2).

## 1.3 NPI Contracts for eBPF Extensions
eBPF Extensions need to implement *provider modules* for two types of NPIs. They are the **Program Information NPI**
provider and the **Hook NPI** provider. The following section explains when an extension must implement these
providers.
eBPF Extensions need to implement *provider modules* for three types of NPIs. They are the **Program Information NPI**
provider, **Hook NPI** provider and **Map Information NPI**.
**Map Information NPI** is optional and only needs to be implemented if an extension want to add support for a
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Map Information NPI** is optional and only needs to be implemented if an extension want to add support for a
**Map Information NPI** is optional and only needs to be implemented if an extension wants to add support for a

grammar

program type specific map. The following section explains when an extension must implement these providers.

### 1.3.1 eBPF Program Information NPI Provider
The eBPF Program Information NPI contract is used to provide information about an eBPF program type. Program types
Expand All @@ -44,12 +45,19 @@ several attach types. The eBPF extension must register a separate Hook NPI provi
supports for an eBPF hook. Note that, there can be more than one attach types for a given program type. If an extension
is adding a new attach type for an existing program type, then it only needs to implement the Hook NPI Provider.

### 1.3.3 eBPF Map Information NPI Provider
The Map Info NPI contract is used by extension to provide an implementation for a map type that is not already
implemented by the eBPF runtime. An example for this can be *BPF_MAP_TYPE_XSKMAP*. The eBPF extension must register
a separate Map Info NPI provider module for each map type it implements.

## 2 Authoring an eBPF Extension
The steps for authoring an eBPF extension are:
1. Register the NPI provider.
2. Author any program type specific Helper Functions.
3. Author any extensible maps.
3. Invoke eBPF programs from hook(s).
4. Register program and attach types.
5. Register extensible map types, if any.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, should this say "program type specific map types" rather than "extensible map types"?


The following sections describe these steps in detail.

Expand Down Expand Up @@ -571,7 +579,95 @@ of time a batch is open and must not change IRQL between calling batch begin and
the number of times the program has been invoked, so callers should limit the number of calls within a batch to
prevent long delays in batch end.

### 2.7 Authoring Helper Functions
### 2.7 Map Information NPI Provider Registration
When registering itself to the NMR, the Map Info NPI provider should have the
[`NPI_REGISTRATION_INSTANCE`](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/netioddk/ns-netioddk-_npi_registration_instance)
initialized as follows:
* `NpiId`: This should be set to `EBPF_MAP_INFO_EXTENSION_IID` defined in `ebpf_extension_uuids.h`.
* `ModuleId`: This can be set to any provider chosen GUID.
* `NpiSpecificCharacteristics`: Pointer to structure of type `ebpf_map_provider_data_t`.

typedef struct _ebpf_map_provider_data
{
ebpf_extension_header_t header;
size_t supported_map_type_count; // Number of supported map types
_Field_size_(supported_map_type_count) const uint32_t* supported_map_types; // Array of supported map types
ebpf_map_provider_dispatch_table_t* dispatch_table;
} ebpf_map_provider_data_t;

#### `ebpf_map_provider_data_t` Struct
This structure is used to specify all the extensible map types that the extension supports. It contains the following fields:
* `supported_map_type_count`
* `supported_map_types`
* `dispatch_table`

The `supported_map_type_count` field contains the number of extensible maps that the extension supports.
The `supported_map_types` is a pointer to an array containing the map types of the extensible maps that the extension supports.
The `dispatch_table` is a pointer to the provider dispatch table that the extension provides for operations on the supported maps.

#### Map ID
eBPF-for-Windows runtime supports some global map types. eBPF-for-Windows has reserved the map IDs 1 to 4095 (BPF_MAP_TYPE_MAX) for the global map types implemented in eBPF Core. Extensions need to use a map ID > BPF_MAP_TYPE_MAX for any extensible map they implement.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eBPF-for-Windows runtime supports some global map types. eBPF-for-Windows has reserved the map IDs 1 to 4095 (BPF_MAP_TYPE_MAX) for the global map types implemented in eBPF Core. Extensions need to use a map ID > BPF_MAP_TYPE_MAX for any extensible map they implement.
The eBPF-for-Windows runtime supports some global map types. eBPF-for-Windows has reserved the map IDs 1 to 4095 (BPF_MAP_TYPE_MAX) for the global map types implemented in eBPF Core. Extensions need to use a map ID > BPF_MAP_TYPE_MAX for any extensible map they implement.

Also I think "Map ID" is the wrong term, since that's the ID for a specific map instance. I think you mean Map Type ID.


Note: Though this is not required, extensions *can* register their map types by creating a pull request to eBPF-for-Windows
repo and updating `ebpf_map_type_t` enum in ebpf_structs.h. This helps in any map type collision with another extension.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
repo and updating `ebpf_map_type_t` enum in ebpf_structs.h. This helps in any map type collision with another extension.
repo and updating the `ebpf_map_type_t` enum in ebpf_structs.h. This helps avoid any map type collision with another extension.


#### `ebpf_map_provider_dispatch_table_t` Struct
```
typedef struct _ebpf_map_provider_dispatch_table
{
ebpf_extension_header_t header;
_Notnull_ ebpf_map_create_t create_map_function;
_Notnull_ ebpf_map_delete_t delete_map_function;
_Notnull_ ebpf_map_associate_program_type_t associate_program_function;
ebpf_map_find_element_t find_element_function;
ebpf_map_update_element_t update_element_function;
ebpf_map_delete_element_t delete_element_function;
ebpf_map_get_next_key_and_value_t get_next_key_and_value_function;
} ebpf_map_provider_dispatch_table_t;
```
This the dispatch table that the extension needs to implement and provide to eBPF runtime. It contains the following fields:
1. `create_map_function` - Called by eBPF runtime to create the map.
2. `delete_map_function` - Called by eBPF runtime to delete the map.
3. `associate_program_function` - Called by eBPF runtime to validate if a specific map can be associated with the supplied program type. eBPFCore invokes this function before an extensible map is associated with a program.
4. `find_element_function` - Function to find an entry.
5. `update_element_function` - Function to update an entry.
5. `delete_element_function` - Function to delete an entry.
6. `get_next_key_and_value_function` - Function to get the next key and value.

When `create_map_function` is invoked, the extension will allocate a map, and return a pointer to it (called `map_context`) back to the eBPF runtime. When any of the APIs are invoked for this map, the extension will get this `map_context` back as an input parameter.

#### `ebpf_map_client_data_t` Struct
`ebpf_map_client_data_t` is the client data that is provided by eBPFCore to the extension when it attaches to the NMR provider. It is defined as below:

```
typedef struct _ebpf_map_client_data
{
ebpf_extension_header_t header; ///< Standard extension header containing version and size information.
uint64_t map_context_offset; ///< Offset within the map structure where the provider context data is stored.
ebpf_map_client_dispatch_table_t* dispatch_table; ///< Pointer to client dispatch table.
} ebpf_map_client_data_t;
```

`map_context_offset` is provided by eBPFCore to the extension to get to the extension specific map context when the
extensible map is being used in a helper function. This value is constant for all the bindings from eBPFCore to the
extension for all extensible map types and instances.

`dispatch_table` is the client dispatch table provided by eBPFCore to the extension. It is defined as below:
```
typedef struct _ebpf_map_client_dispatch_table
{
ebpf_extension_header_t header;
epoch_allocate_with_tag_t epoch_allocate_with_tag;
epoch_allocate_cache_aligned_with_tag_t epoch_allocate_cache_aligned_with_tag;
epoch_free_t epoch_free;
epoch_free_cache_aligned_t epoch_free_cache_aligned;
} ebpf_map_client_dispatch_table_t;
```
The client dispatch table provides *epoch based memory management* APIs that extension can use for allocating
memory when implementing extensible maps.
See [Epoch based memory management](https://github.com/microsoft/ebpf-for-windows/blob/main/docs/EpochBasedMemoryManagement.md) for more details on this topic.

### 2.8 Authoring Helper Functions
An extension can provide an implementation of helper functions that can be invoked by the eBPF programs. The helper
functions can be of two types:
1. Program-Type specific: These helper functions can only be invoked by eBPF programs of a given program type. Usually,
Expand All @@ -597,7 +693,15 @@ The helper function ID for a general helper function must be in the range 0 - 65
The parameter and return types for these helper functions must adhere to the `ebpf_argument_type_t` and
`ebpf_return_type_t` enums.

### 2.8 Registering Program Types and Attach Types - eBPF Store
### 2.9 Helper functions that use extensible maps.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### 2.9 Helper functions that use extensible maps.
### 2.9 Helper functions that use extensible maps

If the extension is implementing a helper function that takes an extensible map as input, when the helper function is
invoked, it will **not** get the map context that it had passed earlier to eBPFCore. It will instead get a pointer to
a separate map structure that eBPFCore maintains. Using this pointer, and the `map_context_offset` provided in the
`map_client_data`, extensions will need to get their map context. `MAP_CONTEXT()` macro is provied in `ebpf_extensions.h`
for extensions to get their map context. Extensions should validate that the map context they got back is NULL or not,
and handle it appropriately.

### 2.9 Registering Program Types and Attach Types - eBPF Store
The eBPF execution context loads an eBPF program from an ELF file that has program section(s) with section names. The
prefix to these names determines the program type. For example, the section name `"xdp"` implies that the corresponding
program type is `BPF_PROG_TYPE_XDP`.
Expand All @@ -622,9 +726,10 @@ To operate on the eBPF store, the user mode application needs to link with eBPFA
_In_reads_(program_info_count) const ebpf_program_info_t* program_info, uint32_t program_info_count);
```

### 2.9 eBPF Sample Driver
### 2.10 eBPF Sample Driver
The eBPF for Windows project provides a
[sample extension driver](https://github.com/microsoft/ebpf-for-windows/tree/8f46b4020f79c32f994d3a59671ce8782e4b4cf0/tests/sample/ext)
as an example for how to implement an extension. This simple extension exposes a new program type, and implements a
hook for it with a single attach type. It implements simple NPI provider modules for the two NPIs. It also implements
three program-type specific helper functions.
The extension also implements two extensible maps, and implements a provider module for the two maps that it implements.
201 changes: 201 additions & 0 deletions include/ebpf_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include "ebpf_structs.h"
#include "ebpf_windows.h"

#define EBPF_MAP_FLAG_HELPER 0x01 /* Called by an eBPF program. */
#define EBPF_MAP_FIND_FLAG_DELETE 0x02 /* Perform a find and delete. */

typedef ebpf_result_t (*_ebpf_extension_dispatch_function)();

typedef struct _ebpf_extension_dispatch_table
Expand Down Expand Up @@ -130,3 +133,201 @@ typedef struct _ebpf_execution_context_state

#define EBPF_CONTEXT_HEADER uint64_t context_header[8]
#define EBPF_CONTEXT_HEADER_SIZE (sizeof(uint64_t) * 8)

/**
* @brief Create an eBPF map.
*
* @param[in] map_type The type of map to create.
* @param[in] key_size The size of the key in bytes.
* @param[in] value_size The size of the value in bytes.
* @param[in] max_entries The maximum number of entries in the map.
* @param[out] map_context The created map context.
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MEMORY Unable to allocate memory.
* @retval EBPF_INVALID_ARGUMENT One or more parameters are incorrect.
*/
typedef ebpf_result_t (*ebpf_map_create_t)(
_In_ void* binding_context,
uint32_t map_type,
uint32_t key_size,
uint32_t value_size,
uint32_t max_entries,
_Outptr_ void** map_context);

/**
* @brief Delete an eBPF map.
*
* @param[in] map_context The map context to delete.
*/
typedef void (*ebpf_map_delete_t)(_In_ _Post_invalid_ void* map_context);

/**
* @brief Find an element in the eBPF map.
*
* @param[in] map The eBPF map to search.
* @param[in] key_size The size of the key in bytes. Set to 0 in case of a helper function call.
* @param[in] key Optionally, the key to search for.
* @param[in] value_size The size of the value in bytes. Set to 0 in case of a helper function call.
* @param[out] value Pointer to the value associated with the key.
* @param[in] flags Find flags.
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_OBJECT_NOT_FOUND The key was not found in the map.
*/
typedef ebpf_result_t (*ebpf_map_find_element_t)(
_In_ void* map,
size_t key_size,
_In_reads_opt_(key_size) const uint8_t* key,
_Outptr_ uint8_t** value,
uint32_t flags);

/**
* @brief Update an element in the eBPF map.
*
* @param[in] map The eBPF map to update.
* @param[in] key_size The size of the key in bytes. Set to 0 in case of a helper function call.
* @param[in] key Optionally, the key to update.
* @param[in] value_size The size of the value in bytes. Set to 0 in case of a helper function call.
* @param[in] value Optionally, the value to associate with the key.
* @param[in] option Update option.
* @param[in] flags Update flags.
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_OBJECT_NOT_FOUND The key was not found in the map.
* @retval EBPF_INVALID_ARGUMENT One or more parameters are incorrect.
* @retval EBPF_NO_MEMORY Unable to allocate memory.
*/
typedef ebpf_result_t (*ebpf_map_update_element_t)(
_In_ void* map,
size_t key_size,
_In_reads_opt_(key_size) const uint8_t* key,
size_t value_size,
_In_reads_opt_(value_size) const uint8_t* value,
ebpf_map_option_t option,
uint32_t flags);

/**
* @brief Delete an element from the eBPF map.
* @param[in] map The eBPF map to delete from.
* @param[in] key_size The size of the key in bytes. Set to 0 in case of a helper function call.
* @param[in] key Optionally, the key to delete. If the key is not found, the map is unchanged. If the key is found, the
* associated value is deleted.
* @param[in] flags Delete flags.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_OBJECT_NOT_FOUND The key was not found in the map.
* @retval EBPF_INVALID_ARGUMENT One or more parameters are incorrect.
* @retval EBPF_NO_MEMORY Unable to allocate memory.
*/
typedef ebpf_result_t (*ebpf_map_delete_element_t)(
_In_ void* map, size_t key_size, _In_reads_opt_(key_size) const uint8_t* key, uint32_t flags);

/**
* @brief Get the next key and value in the eBPF map.
*
* @param[in] map The eBPF map to query.
* @param[in] previous_key The previous key. If NULL, get the first key.
* @param[out] next_key The next key in the map.
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_OBJECT_NOT_FOUND No more keys in the map.
* @retval EBPF_INVALID_ARGUMENT One or more parameters are incorrect.
*/
typedef ebpf_result_t (*ebpf_map_get_next_key_and_value_t)(
_In_ void* map,
size_t key_size,
_In_reads_opt_(key_size) const uint8_t* previous_key,
_Out_writes_(key_size) uint8_t* next_key,
_Outptr_opt_ uint8_t** next_value);

/**
* @brief Get the next key in the eBPF map.
*
* @param[in] map The eBPF map to query.
* @param[in] program_type The program type.
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_OPERATION_NOT_SUPPORTED The operation is not supported.
*/
typedef ebpf_result_t (*ebpf_map_associate_program_type_t)(
_In_ void* map, _In_ const ebpf_program_type_t* program_type);

/**
* Dispatch table implemented by the eBPF extension to provide map operations.
* This table is used to provide map operations to the eBPF core.
*/
typedef struct _ebpf_map_provider_dispatch_table
{
ebpf_extension_header_t header;
_Notnull_ ebpf_map_create_t create_map_function;
_Notnull_ ebpf_map_delete_t delete_map_function;
_Notnull_ ebpf_map_associate_program_type_t associate_program_function;
ebpf_map_find_element_t find_element_function;
ebpf_map_update_element_t update_element_function;
ebpf_map_delete_element_t delete_element_function;
ebpf_map_get_next_key_and_value_t get_next_key_and_value_function;
} ebpf_map_provider_dispatch_table_t;

/**
* @brief Allocate memory under epoch control.
* @param[in] size Size of memory to allocate
* @param[in] tag Pool tag to use.
* @returns Pointer to memory block allocated, or null on failure.
*/
typedef _Ret_writes_maybenull_(size) void* (*epoch_allocate_with_tag_t)(size_t size, uint32_t tag);

/**
* @brief Allocate cache aligned memory under epoch control.
* @param[in] size Size of memory to allocate
* @param[in] tag Pool tag to use.
* @returns Pointer to memory block allocated, or null on failure.
*/
typedef _Ret_writes_maybenull_(size) void* (*epoch_allocate_cache_aligned_with_tag_t)(size_t size, uint32_t tag);

/**
* @brief Free memory under epoch control.
* @param[in] memory Allocation to be freed once epoch ends.
*/
typedef void (*epoch_free_t)(_In_opt_ void* memory);

/**
* @brief Free memory under epoch control.
* @param[in] memory Allocation to be freed once epoch ends.
*/
typedef void (*epoch_free_cache_aligned_t)(_In_opt_ void* pointer);

typedef ebpf_result_t (*ebpf_get_map_context_t)(_In_ const void* map, _Outptr_ void** map_context);

/**
* Dispatch table implemented by the eBPF runtime to provide RCU / epoch operations.
*/
typedef struct _ebpf_map_client_dispatch_table
{
ebpf_extension_header_t header;
epoch_allocate_with_tag_t epoch_allocate_with_tag;
epoch_allocate_cache_aligned_with_tag_t epoch_allocate_cache_aligned_with_tag;
epoch_free_t epoch_free;
epoch_free_cache_aligned_t epoch_free_cache_aligned;
} ebpf_map_client_dispatch_table_t;

/**
* @brief Extensible map provider data.
*/
typedef struct _ebpf_map_provider_data
{
ebpf_extension_header_t header;
uint32_t map_type; // Extensible map type implemented by the provider.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint32_t map_type; // Extensible map type implemented by the provider.
uint32_t map_type; // Custom map type implemented by the provider.

ebpf_map_provider_dispatch_table_t* dispatch_table;
} ebpf_map_provider_data_t;

/**
* @brief Extensible map client data.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @brief Extensible map client data.
* @brief Custom map client data.

Update to match PR #4882

*/
typedef struct _ebpf_map_client_data
{
ebpf_extension_header_t header; ///< Standard extension header containing version and size information.
uint64_t map_context_offset; ///< Offset within the map structure where the provider context data is stored.
ebpf_map_client_dispatch_table_t* dispatch_table; ///< Pointer to client dispatch table.
} ebpf_map_client_data_t;

#define MAP_CONTEXT(map_pointer, offset) ((void**)(((uint8_t*)(map_pointer)) + (offset)))
5 changes: 5 additions & 0 deletions include/ebpf_extension_uuids.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ extern "C"
__declspec(selectany) GUID EBPF_HOOK_EXTENSION_IID = {
0x5d564054, 0x2736, 0x406d, {0x8b, 0x22, 0x12, 0xbc, 0xff, 0xaf, 0x0a, 0x9f}};

/** @brief NPI ID for eBPF map extension.
* 0x3c2b0f4e, 0x8b0e, 0x4e5e, 0x8e1b, 0x7f4b1f0e1f0e
*/
__declspec(selectany) GUID EBPF_MAP_INFO_EXTENSION_IID = {
0x3c2b0f4e, 0x8b0e, 0x4e5e, {0x8e, 0x1b, 0x7f, 0x4b, 0x1f, 0x0e, 0x1f, 0x0e}};
#ifdef __cplusplus
}
#endif
Loading
Loading