Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global variable support #886

Closed
wants to merge 13 commits into from
39 changes: 39 additions & 0 deletions docs/NativeCodeGeneration.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ typedef struct _metadata_table
{
void (*programs)(program_entry_t** programs, size_t* count);
void (*maps)(map_entry_t** maps, size_t* count);
void (*hash)(const uint8_t** hash, size_t* size);
void (*version)(bpf2c_version_t* version);
void (*map_initial_values)(map_initial_values_t** map_initial_values, size_t* count);
void (*global_variable_sections)(global_variable_section_t** global_variable_sections, size_t* count);
} metadata_table_t;

metadata_table_t myprogram_metadata_table = { _get_programs, _get_maps };
Expand All @@ -115,6 +119,7 @@ Each program in the generated C file is exported via a program_entry_t:
typedef struct _program_entry
{
uint64_t (*function)(void*);
const char* pe_section_name;
const char* section_name;
const char* program_name;
uint16_t* referenced_map_indices;
Expand Down Expand Up @@ -179,6 +184,40 @@ The skeleton then uses NMR to either create an instance of the map or obtain the
write the address into the address field. References to the maps in the generated code are then indirect via the
address field.

### Map initial values

Hash of map, array of map, and program array maps can have initial values defined. For each map that has initial
values provided, an entry of type map_initial_values_t is added.

```c
typedef struct _map_initial_values
{
const char* name;
size_t count;
const char** values;
} map_initial_values_t;
```

The name field is the name of the map for which initial values are being provided.
The count and values field are an array of map names to initialize the map with.

### Exported global variable sections

Each global section (.rodata, .data, or .bss) that is referenced by the program is added as an entry.

```c
typedef struct _global_variable_section
{
const char* name;
unsigned char* address_of_map_value;
size_t size;
const void* initial_data;
} global_variable_section_t;
```

The execution context creates an array map for each section, loads any initial data into the map, and stores the
address of the start of the map data into the address_of_map_value field.

## Loading an eBPF program from a PE .sys file

The process of loading an eBPF program is a series of interactions between the eBPF Execution Context and the generated
Expand Down
11 changes: 11 additions & 0 deletions include/bpf2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ extern "C"
const char** values; // Array of strings containing the initial values.
} map_initial_values_t;

typedef struct _global_variable_section
{
const char* name;
unsigned char* address_of_map_value;
size_t size;
const void* initial_data;
} global_variable_section_t;

/**
* @brief Program entry.
* This structure contains the address of the program and additional information about the program.
Expand Down Expand Up @@ -146,6 +154,9 @@ extern "C"
void (*map_initial_values)(
_Outptr_result_buffer_maybenull_(*count) map_initial_values_t** map_initial_values,
_Out_ size_t* count); ///< Returns the list of initial values for maps in this module.
void (*global_variable_sections)(
_Outptr_result_buffer_maybenull_(*count) global_variable_section_t** global_variable_sections,
_Out_ size_t* count); ///< Returns the list of global variables in this module.
} metadata_table_t;

/**
Expand Down
2 changes: 2 additions & 0 deletions libs/api/ebpf_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2493,6 +2493,8 @@ _ebpf_pe_get_map_definitions(
for (int map_index = 0; map_offset + sizeof(map_entry_t) <= section_header.Misc.VirtualSize;
map_offset += sizeof(map_entry_t), map_index++) {
map_entry_t* entry = (map_entry_t*)(buffer->buf + map_offset);
// Issue: https://github.com/microsoft/ebpf-for-windows/issues/4168
// This heuristic is fragile and will break when map_entry_t changes.
if (entry->address != nullptr) {
// bpf2c generates a section that has map names longer than sizeof(map_entry_t)
// at the end of the section. This entry seems to be a map name string, so we've
Expand Down
33 changes: 33 additions & 0 deletions libs/execution_context/ebpf_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,39 @@ ebpf_core_resolve_maps(
EBPF_RETURN_RESULT(return_value);
}

_Must_inspect_result_ ebpf_result_t
ebpf_core_resolve_map_value_address(
uint32_t count_of_maps,
_In_reads_(count_of_maps) const ebpf_handle_t* map_handles,
_Out_writes_(count_of_maps) uintptr_t* map_addresses)
{
EBPF_LOG_ENTRY();
uint32_t map_index = 0;
ebpf_result_t return_value = EBPF_SUCCESS;

for (map_index = 0; map_index < count_of_maps; map_index++) {
ebpf_map_t* map;
return_value =
EBPF_OBJECT_REFERENCE_BY_HANDLE(map_handles[map_index], EBPF_OBJECT_MAP, (ebpf_core_object_t**)&map);

if (return_value != EBPF_SUCCESS) {
goto Done;
}

return_value = ebpf_map_get_value_address(map, &map_addresses[map_index]);

// First release the map reference, then check the return value.
EBPF_OBJECT_RELEASE_REFERENCE((ebpf_core_object_t*)map);

if (return_value != EBPF_SUCCESS) {
goto Done;
}
}

Done:
EBPF_RETURN_RESULT(return_value);
}

#if !defined(CONFIG_BPF_JIT_DISABLED)
static ebpf_result_t
_ebpf_core_protocol_resolve_map(
Expand Down
19 changes: 19 additions & 0 deletions libs/execution_context/ebpf_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,25 @@ extern "C"
ebpf_core_update_map_with_handle(
ebpf_handle_t map_handle, _In_ const uint8_t* key, size_t key_length, ebpf_handle_t value);

/**
* @brief Resolve the provided map handles to address of the first value in the map if
* the map is an array map or NULL if the map is not an array map.
*
* @param[in] program_handle Handle of the program to associate maps with.
* @param[in] count_of_maps Number of map handles.
* @param[in] map_handles Array of map handles containing "count_of_maps" handles.
* @param[out] map_addresses Array of map value addresses of size "count_of_maps"
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_INVALID_OBJECT The provided handle is not valid.
* @retval EBPF_NO_MEMORY Unable to allocate resources for this operation.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_core_resolve_map_value_address(
uint32_t count_of_maps,
_In_reads_(count_of_maps) const ebpf_handle_t* map_handles,
_Out_writes_(count_of_maps) uintptr_t* map_value_addresses);

#ifdef __cplusplus
}
#endif
12 changes: 12 additions & 0 deletions libs/execution_context/ebpf_maps.c
Original file line number Diff line number Diff line change
Expand Up @@ -3033,3 +3033,15 @@ ebpf_map_get_next_key_and_value_batch(

return result;
}

_Must_inspect_result_ ebpf_result_t
ebpf_map_get_value_address(_In_ const ebpf_map_t* map, _Out_ uintptr_t* value_address)
{
if (map->ebpf_map_definition.type != BPF_MAP_TYPE_ARRAY) {
return EBPF_INVALID_ARGUMENT;
} else {
ebpf_core_map_t* core_map = (ebpf_core_map_t*)map;
*value_address = (uintptr_t)core_map->data;
}
return EBPF_SUCCESS;
}
12 changes: 12 additions & 0 deletions libs/execution_context/ebpf_maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,18 @@ extern "C"
_Out_writes_bytes_to_(*key_and_value_length, *key_and_value_length) uint8_t* key_and_value,
int flags);

/**
* @brief Get the address of the first value in the map if it is an array or
* return EBPF_INVALID_ARGUMENT if it is not an array map.
*
* @param[in] map Map to query.
* @param[out] value_address Map value address.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_INVALID_ARGUMENT The provided map is not valid.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_map_get_value_address(_In_ const ebpf_map_t* map, _Out_ uintptr_t* value_address);

#ifdef __cplusplus
}
#endif
58 changes: 57 additions & 1 deletion libs/execution_context/ebpf_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ static NPI_PROVIDER_ATTACH_CLIENT_FN _ebpf_native_provider_attach_client_callbac
static NPI_PROVIDER_DETACH_CLIENT_FN _ebpf_native_provider_detach_client_callback;

static const NPI_PROVIDER_CHARACTERISTICS _ebpf_native_provider_characteristics = {
0,
1,
sizeof(_ebpf_native_provider_characteristics),
_ebpf_native_provider_attach_client_callback,
_ebpf_native_provider_detach_client_callback,
Expand Down Expand Up @@ -432,6 +432,14 @@ _ebpf_native_map_initial_values_fallback(
*count = 0;
}

static void
_ebpf_native_global_variable_sections_fallback(
_Outptr_result_buffer_maybenull_(*count) global_variable_section_t** global_variable_sections, _Out_ size_t* count)
{
global_variable_sections = NULL;
*count = 0;
}

static NTSTATUS
_ebpf_native_provider_attach_client_callback(
_In_ HANDLE nmr_binding_handle,
Expand Down Expand Up @@ -498,6 +506,10 @@ _ebpf_native_provider_attach_client_callback(
client_context->table.map_initial_values = _ebpf_native_map_initial_values_fallback;
}

if (!client_context->table.global_variable_sections) {
client_context->table.global_variable_sections = _ebpf_native_global_variable_sections_fallback;
}

ebpf_lock_create(&client_context->lock);
client_context->base.marker = _ebpf_native_marker;
client_context->base.acquire_reference = _ebpf_native_acquire_reference_internal;
Expand Down Expand Up @@ -945,6 +957,39 @@ _ebpf_native_set_initial_map_values(_Inout_ ebpf_native_module_t* module)
EBPF_RETURN_RESULT(result);
}

static ebpf_result_t
_ebpf_native_initialize_global_variables(_Inout_ ebpf_native_module_t* module)
{
EBPF_LOG_ENTRY();
ebpf_result_t result = EBPF_SUCCESS;
global_variable_section_t* global_variables = NULL;
size_t global_variable_count = 0;

// Get global variables.
module->table.global_variable_sections(&global_variables, &global_variable_count);

// For each entry.
for (size_t i = 0; i < global_variable_count; i++) {
ebpf_native_map_t* native_map = _ebpf_native_find_map_by_name(module, global_variables[i].name);
if (native_map == NULL) {
result = EBPF_INVALID_ARGUMENT;
break;
}

// Resolve initial value address.
result = ebpf_core_resolve_map_value_address(
1, &native_map->handle, (uintptr_t*)&global_variables[i].address_of_map_value);
if (result != EBPF_SUCCESS) {
break;
}

// Copy the initial value to the map.
memcpy(global_variables[i].address_of_map_value, global_variables[i].initial_data, global_variables[i].size);
}

EBPF_RETURN_RESULT(result);
}

static ebpf_result_t
_ebpf_native_create_maps(_Inout_ ebpf_native_module_t* module)
{
Expand Down Expand Up @@ -1635,6 +1680,17 @@ ebpf_native_load_programs(
goto Done;
}

// Setup global variables.
result = _ebpf_native_initialize_global_variables(module);
if (result != EBPF_SUCCESS) {
EBPF_LOG_MESSAGE_GUID(
EBPF_TRACELOG_LEVEL_VERBOSE,
EBPF_TRACELOG_KEYWORD_NATIVE,
"ebpf_native_load_programs: resolve map value addresses failed",
module_id);
goto Done;
}

module_state = ebpf_lock_lock(&module->lock);
native_lock_acquired = true;

Expand Down
17 changes: 16 additions & 1 deletion tests/bpf2c_tests/expected/atomic_instruction_fetch_add_dll.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ _get_maps(_Outptr_result_buffer_maybenull_(*count) map_entry_t** maps, _Out_ siz
*count = 1;
}

static void
_get_global_variable_sections(
_Outptr_result_buffer_maybenull_(*count) global_variable_section_t** global_variable_sections, _Out_ size_t* count)
{
*global_variable_sections = NULL;
*count = 0;
}

static helper_function_entry_t func_helpers[] = {
{NULL, 1, "helper_id_1"},
};
Expand Down Expand Up @@ -197,4 +205,11 @@ _get_map_initial_values(_Outptr_result_buffer_(*count) map_initial_values_t** ma
}

metadata_table_t atomic_instruction_fetch_add_metadata_table = {
sizeof(metadata_table_t), _get_programs, _get_maps, _get_hash, _get_version, _get_map_initial_values};
sizeof(metadata_table_t),
_get_programs,
_get_maps,
_get_hash,
_get_version,
_get_map_initial_values,
_get_global_variable_sections,
};
17 changes: 16 additions & 1 deletion tests/bpf2c_tests/expected/atomic_instruction_fetch_add_raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ _get_maps(_Outptr_result_buffer_maybenull_(*count) map_entry_t** maps, _Out_ siz
*count = 1;
}

static void
_get_global_variable_sections(
_Outptr_result_buffer_maybenull_(*count) global_variable_section_t** global_variable_sections, _Out_ size_t* count)
{
*global_variable_sections = NULL;
*count = 0;
}

static helper_function_entry_t func_helpers[] = {
{NULL, 1, "helper_id_1"},
};
Expand Down Expand Up @@ -167,4 +175,11 @@ _get_map_initial_values(_Outptr_result_buffer_(*count) map_initial_values_t** ma
}

metadata_table_t atomic_instruction_fetch_add_metadata_table = {
sizeof(metadata_table_t), _get_programs, _get_maps, _get_hash, _get_version, _get_map_initial_values};
sizeof(metadata_table_t),
_get_programs,
_get_maps,
_get_hash,
_get_version,
_get_map_initial_values,
_get_global_variable_sections,
};
17 changes: 16 additions & 1 deletion tests/bpf2c_tests/expected/atomic_instruction_fetch_add_sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ _get_maps(_Outptr_result_buffer_maybenull_(*count) map_entry_t** maps, _Out_ siz
*count = 1;
}

static void
_get_global_variable_sections(
_Outptr_result_buffer_maybenull_(*count) global_variable_section_t** global_variable_sections, _Out_ size_t* count)
{
*global_variable_sections = NULL;
*count = 0;
}

static helper_function_entry_t func_helpers[] = {
{NULL, 1, "helper_id_1"},
};
Expand Down Expand Up @@ -328,4 +336,11 @@ _get_map_initial_values(_Outptr_result_buffer_(*count) map_initial_values_t** ma
}

metadata_table_t atomic_instruction_fetch_add_metadata_table = {
sizeof(metadata_table_t), _get_programs, _get_maps, _get_hash, _get_version, _get_map_initial_values};
sizeof(metadata_table_t),
_get_programs,
_get_maps,
_get_hash,
_get_version,
_get_map_initial_values,
_get_global_variable_sections,
};
17 changes: 16 additions & 1 deletion tests/bpf2c_tests/expected/bad_map_name_dll.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ _get_maps(_Outptr_result_buffer_maybenull_(*count) map_entry_t** maps, _Out_ siz
*count = 1;
}

static void
_get_global_variable_sections(
_Outptr_result_buffer_maybenull_(*count) global_variable_section_t** global_variable_sections, _Out_ size_t* count)
{
*global_variable_sections = NULL;
*count = 0;
}

static helper_function_entry_t lookup_helpers[] = {
{NULL, 1, "helper_id_1"},
};
Expand Down Expand Up @@ -197,4 +205,11 @@ _get_map_initial_values(_Outptr_result_buffer_(*count) map_initial_values_t** ma
}

metadata_table_t bad_map_name_metadata_table = {
sizeof(metadata_table_t), _get_programs, _get_maps, _get_hash, _get_version, _get_map_initial_values};
sizeof(metadata_table_t),
_get_programs,
_get_maps,
_get_hash,
_get_version,
_get_map_initial_values,
_get_global_variable_sections,
};
Loading
Loading