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

Make the JIT'd code completely portable. #427

Merged
merged 2 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_subdirectory("vm")
STEP_TARGETS build)

if(UBPF_ENABLE_TESTS)
add_subdirectory("custom_tests")
add_subdirectory("ubpf_plugin")
if (NOT UBPF_SKIP_EXTERNAL)
endif()
Expand Down
3 changes: 2 additions & 1 deletion cmake/settings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
-Wall
-Werror
-Iinc
-O2
-O0
-g
-Wunused-parameter
-fPIC
)
Expand Down
62 changes: 62 additions & 0 deletions custom_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright (c) Microsoft Corporation
# SPDX-License-Identifier: Apache-2.0

set(CMAKE_CXX_STANDARD 20)

file(GLOB test_descr_files ${CMAKE_SOURCE_DIR}/custom_tests/descrs/*.md)

add_library(ubpf_custom_test_support srcs/ubpf_custom_test_support.cc)

target_link_libraries(
ubpf_custom_test_support
ubpf
ubpf_settings
)

target_include_directories(ubpf_custom_test_support PUBLIC ".srcs/")
target_include_directories(ubpf_custom_test_support PRIVATE
"${CMAKE_SOURCE_DIR}/vm"
"${CMAKE_BINARY_DIR}/vm"
"${CMAKE_SOURCE_DIR}/vm/inc"
"${CMAKE_BINARY_DIR}/vm/inc"
)

set(QEMU_RUNNER "")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 AND (NOT CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL aarch64))
set(QEMU_RUNNER qemu-aarch64 -L /usr/aarch64-linux-gnu)
endif()

foreach(test_file ${test_descr_files})
get_filename_component(test_name ${test_file} NAME_WE)
set(test_source_path "${CMAKE_SOURCE_DIR}/custom_tests/srcs/${test_name}.cc")

add_executable(
${test_name}
${test_source_path}
)
target_include_directories(${test_name} PRIVATE
"${CMAKE_SOURCE_DIR}/vm"
"${CMAKE_BINARY_DIR}/vm"
"${CMAKE_SOURCE_DIR}/vm/inc"
"${CMAKE_BINARY_DIR}/vm/inc"
)
target_link_libraries(
${test_name}
ubpf
ubpf_custom_test_support
ubpf_settings
)
set(potential_input_file ${CMAKE_SOURCE_DIR}/custom_tests/data/${test_name}.input)
if (EXISTS ${potential_input_file})
list(JOIN QEMU_RUNNER " " QEMU_RUNNER_STR)
add_test(
NAME ${test_name}-Custom
COMMAND sh -c "cat ${potential_input_file} | ${QEMU_RUNNER_STR} $<TARGET_FILE:${test_name}>"
)
else()
add_test(
NAME ${test_name}-Custom
COMMAND ${QEMU_RUNNER} $<TARGET_FILE:${test_name}>
)
endif()
endforeach()
35 changes: 35 additions & 0 deletions custom_tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Writing a uBPF Custom Tests

Custom tests are enabled by creating two (2) or three (3) different files in the `custom_tests` directory.

### Files Of a uBPF Custom Test

#### Description Files

The first file to create is the Description File. The Description File is a file with a `.md` extension that resides in the `descrs` directory. The purpose of this file is to identify the name of the test (everything before the `.md` extension) and provide a place to document the purpose of the test.

#### Source Files

The second file to create is the Source File. The Source file should reside in the `srcs` directory and have a name that matches its Description File (with the `.cc` extension rather than the `.md` extension).

#### Input Files

The final file is optional. The Input File resides in the `data` directory and should have the same name as the other two (2) files but with an `.input` extension rather than `.cc` or `.md` for the Source and Description File respectively. If present, the contents of this file will be given to the executed custom test over standard input.

### Building

The Source Files for a custom test are compiled using C++20 and are saved as an executable named according to the name of the test in the CMake build directory.

### Return Values

All successful tests should return `0`. All failing tests should return something other than `0`.

### Supporting Libraries

To reduce the boilerplate needed to write custom tests, there is a custom test library with several helpful functions. These functions are documented in the library's header file (`custom_tests/srcs/ubpf_custom_test_support.h`).

### Putting It Together

After describing the test's purpose in a Markdown syntax in a file named, say, `test_example.md` and stored in the `descrs` directory, you can write the test's Source Code (in C++20) and give it the name `test_example.cc` in the `srcs` directory. If the test needs input, you can save that input in the tests Input File (`test_input.input`) in the `data` directory.

Because all the files are present, this test will be run when the CTest target is invoked. Because there the optional `test_input.input` file is present, the contents of that file will be given to the executable via standard input.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b7 01 00 00 01 02 03 04 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8f 00 00 00 01 00 00 00
1 change: 1 addition & 0 deletions custom_tests/data/ubpf_test_update_dispatcher.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00
1 change: 1 addition & 0 deletions custom_tests/data/ubpf_test_update_helpers.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Test Description

This custom test program tests whether JIT'd eBPF programs properly pass the original context
to external helper dispatcher even when (eBPF) register r0 has been modified. The original
context to the eBPF program is passed in (eBPF) register r0. Subsequent changes to that
register by the eBPF program should *not* affect that context (which is given to the
helper function external dispatcher).
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test Description

This custom test program tests whether JIT'd eBPF programs properly pass the context
to external helper dispatcher.
4 changes: 4 additions & 0 deletions custom_tests/descrs/ubpf_test_jit_buffer_too_small.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test Description

This custom test program tests whether compilation fails (with the proper error message) when
the user gives a buffer that is too small to accommodate the size of the JIT'd code.
4 changes: 4 additions & 0 deletions custom_tests/descrs/ubpf_test_jit_unexpected_instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test Description

This custom test program tests that an eBPF program fails to load (with the proper error) in
the presence of a program with an invalid instruction opcode.
4 changes: 4 additions & 0 deletions custom_tests/descrs/ubpf_test_update_dispatcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test Description

This custom test program tests whether it is possible to update the external helper dispatcher
after an eBPF program has been compiled.
4 changes: 4 additions & 0 deletions custom_tests/descrs/ubpf_test_update_helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Test Description

This custom test program tests whether it is possible to update the external helper
functions for an eBPF program that has already been JIT'd.
225 changes: 225 additions & 0 deletions custom_tests/srcs/test_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once
#include "ubpf.h"
#include <string.h>
#include <cmath>
#include <cstdint>
#include <map>

#if !defined(UNREFERENCED_PARAMETER)
#define UNREFERENCED_PARAMETER(P) (void)(P)
#endif

static uint64_t
gather_bytes(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
return ((uint64_t)(a & 0xff) << 32) | ((uint64_t)(b & 0xff) << 24) | ((uint64_t)(c & 0xff) << 16) |
((uint64_t)(d & 0xff) << 8) | (e & 0xff);
};

static uint64_t
memfrob(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);

uint8_t* p = reinterpret_cast<uint8_t*>(a);
for (uint64_t i = 0; i < b; i++) {
p[i] ^= 42;
}
return 0;
};

;

static uint64_t
no_op(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);

return 0;
}

static uint64_t
sqrti(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);

return static_cast<uint64_t>(std::sqrt(a));
}

static uint64_t
strcmp_ext(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return strcmp(reinterpret_cast<char*>(a), reinterpret_cast<char*>(b));
}

static uint64_t
unwind(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return a;
}

static std::map<uint32_t, external_function_t> helper_functions = {
{0, gather_bytes},
{1, memfrob},
{2, no_op},
{3, sqrti},
{4, strcmp_ext},
{5, unwind},
};

static uint64_t
dispatcher_test_memfrob(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 42;

}

static uint64_t
updated_dispatcher_test_memfrob(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 43;
}

static uint64_t
dispatcher_gather_bytes(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 44;

}

static uint64_t
updated_dispatcher_gather_bytes(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 45;
}

static uint64_t
dispatcher_no_op(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 46;

}

static uint64_t
updated_dispatcher_no_op(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 47;
}

static uint64_t
dispatcher_sqrti(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 48;

}

static uint64_t
updated_dispatcher_sqrti(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 49;
}

static uint64_t
dispatcher_strcmp_ext(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 50;

}

static uint64_t
updated_dispatcher_strcmp_ext(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 51;
}

static uint64_t
dispatcher_unwind(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 52;

}

static uint64_t
updated_dispatcher_unwind(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e)
{
UNREFERENCED_PARAMETER(a);
UNREFERENCED_PARAMETER(b);
UNREFERENCED_PARAMETER(c);
UNREFERENCED_PARAMETER(d);
UNREFERENCED_PARAMETER(e);
return 53;
}
Loading
Loading