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

Add support for statically initializing BPF_MAP_TYPE_HASH_OF_MAPS #3211

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion libs/execution_context/ebpf_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ _ebpf_native_set_initial_map_values(_Inout_ ebpf_native_module_t* module)

ebpf_handle_t handle_to_insert = ebpf_handle_invalid;

if (native_map_to_update->entry->definition.type == BPF_MAP_TYPE_ARRAY_OF_MAPS) {
if (_ebpf_native_is_map_in_map(native_map_to_update)) {
ebpf_native_map_t* native_map_to_insert =
_ebpf_native_find_map_by_name(module, map_initial_values[i].values[j]);
if (native_map_to_update == NULL) {
Expand Down
48 changes: 48 additions & 0 deletions tests/sample/undocked/hash_of_map.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT

// Whenever this sample program changes, bpf2c_tests will fail unless the
// expected files in tests\bpf2c_tests\expected are updated. The following
// script can be used to regenerate the expected files:
// generate_expected_bpf2c_output.ps1
//
// Usage:
// .\scripts\generate_expected_bpf2c_output.ps1 <build_output_path>
// Example:
// .\scripts\generate_expected_bpf2c_output.ps1 .\x64\Debug\

#include "bpf_helpers.h"
#include "sample_ext_helpers.h"

struct
{
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, uint32_t);
__type(value, uint32_t);
__uint(max_entries, 1);
} inner_map SEC(".maps");

struct
{
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
__type(key, uint32_t);
__type(value, uint32_t);
__uint(max_entries, 1);
__array(values, inner_map);
} outer_map SEC(".maps") = {
.values = {&inner_map},
};

SEC("sample_ext") int lookup(sample_program_context_t* ctx)
{
uint32_t outer_key = 0;
void* inner_map = bpf_map_lookup_elem(&outer_map, &outer_key);
if (inner_map) {
uint32_t inner_key = 0;
uint32_t* value = (uint32_t*)bpf_map_lookup_elem(inner_map, &inner_key);
if (value) {
return *(uint32_t*)value;
}
}
return 0;
}
61 changes: 61 additions & 0 deletions tests/unit/libbpf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma warning(pop)
#include "capture_helper.hpp"
#include "catch_wrapper.hpp"
#include "common_tests.h"
#include "ebpf_platform.h"
#include "ebpf_tracelog.h"
#include "ebpf_vm_isa.hpp"
Expand Down Expand Up @@ -3293,4 +3294,64 @@ TEST_CASE("libbpf map batch", "[libbpf]")

delete_batch_size = batch_size;
REQUIRE(bpf_map_delete_batch(invalid_map_fd, keys.data(), &delete_batch_size, &opts) == -EBADF);
}

void
_hash_of_map_initial_value_test(ebpf_execution_type_t execution_type)
{
_test_helper_libbpf test_helper;
test_helper.initialize();

std::string file_name = std::format("hash_of_map{}", execution_type == EBPF_EXECUTION_NATIVE ? "_um.dll" : ".o");

bpf_object_ptr object(bpf_object__open(file_name.c_str()));
REQUIRE(object != nullptr);

REQUIRE(ebpf_object_set_execution_type(object.get(), execution_type) == EBPF_SUCCESS);

// Load the BPF program.
REQUIRE(bpf_object__load(object.get()) == 0);

// Get the outer map.
bpf_map* outer_map = bpf_object__find_map_by_name(object.get(), "outer_map");
REQUIRE(outer_map != nullptr);

bpf_map* inner_map = bpf_object__find_map_by_name(object.get(), "inner_map");
REQUIRE(inner_map != nullptr);

fd_t outer_map_fd = bpf_map__fd(outer_map);
REQUIRE(outer_map_fd > 0);

fd_t inner_map_fd = bpf_map__fd(inner_map);
REQUIRE(inner_map_fd > 0);

// Issue: https://github.com/microsoft/ebpf-for-windows/issues/3210
// Only native execution supports map of maps with static initializers.
if (execution_type != EBPF_EXECUTION_NATIVE) {
return;
}

uint32_t key = 0;
uint32_t inner_map_id = 0;

// Get the map at index 0.
REQUIRE(bpf_map_lookup_elem(outer_map_fd, &key, &inner_map_id) == 0);

// Get id of the inner map.
bpf_map_info info;
uint32_t info_length = sizeof(info);
memset(&info, 0, sizeof(info));
REQUIRE(bpf_obj_get_info_by_fd(inner_map_fd, &info, &info_length) == 0);

// Verify that the id of the inner map matches the id in the outer map.
REQUIRE(inner_map_id == info.id);
}

TEST_CASE("hash_of_map", "[libbpf]")
{
#if !defined(CONFIG_BPF_JIT_DISABLED)
_hash_of_map_initial_value_test(EBPF_EXECUTION_JIT);

#endif
_hash_of_map_initial_value_test(EBPF_EXECUTION_NATIVE);
}
5 changes: 5 additions & 0 deletions tools/bpf2c/bpf_code_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,11 @@ bpf_code_generator::parse_btf_maps_section(const unsafe_string& name)
// Compute the offset of the values array and resize the vector
// to hold the initial values.
if (member.name == "values") {
// If the map is statically initialized, then the keys must be uint32_t.
Alan-Jowett marked this conversation as resolved.
Show resolved Hide resolved
if (map_definition.key_size != sizeof(uint32_t)) {
throw bpf_code_generator_exception("map keys must be uint32_t for static initialization");
}

map_names_to_values_offset[unsafe_symbol_name] = member.offset_from_start_in_bits / 8;
if (map_names_to_values_offset[unsafe_symbol_name] > (range.second - range.first)) {
throw bpf_code_generator_exception("map values offset is outside of map range");
Expand Down