640 строки
22 KiB
C++
640 строки
22 KiB
C++
// Copyright (c) Microsoft Corporation
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <filesystem>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <sys/stat.h>
|
|
#include <vector>
|
|
#include "api_common.hpp"
|
|
#include "api_internal.h"
|
|
#include "ebpf_api.h"
|
|
#include "ebpf_platform.h"
|
|
#include "ebpf_program_types.h"
|
|
#include "ebpf_verifier_wrapper.hpp"
|
|
#include "elfio_wrapper.hpp"
|
|
#include "ElfWrapper.h"
|
|
#include "platform.hpp"
|
|
#include "windows_platform.hpp"
|
|
#include "windows_platform_common.hpp"
|
|
#include "Verifier.h"
|
|
|
|
#define elf_everparse_error ElfEverParseError
|
|
#define elf_everparse_verify ElfCheckElf
|
|
|
|
thread_local static std::string _elf_everparse_error;
|
|
|
|
extern "C" void
|
|
elf_everparse_error(_In_ const char* struct_name, _In_ const char* field_name, _In_ const char* reason);
|
|
|
|
void
|
|
elf_everparse_error(_In_ const char* struct_name, _In_ const char* field_name, _In_ const char* reason)
|
|
{
|
|
_elf_everparse_error =
|
|
std::string() + "Failed parsing in struct " + struct_name + " field " + field_name + " reason " + reason;
|
|
}
|
|
|
|
using namespace std;
|
|
|
|
typedef struct _section_program_map
|
|
{
|
|
string section_name;
|
|
string program_name;
|
|
} section_program_map_t;
|
|
|
|
typedef struct _section_offset_to_map
|
|
{
|
|
size_t section_offset;
|
|
string map_name;
|
|
} section_offset_to_map_t;
|
|
|
|
struct _thread_local_storage_cache
|
|
{
|
|
~_thread_local_storage_cache() { ebpf_clear_thread_local_storage(); }
|
|
};
|
|
|
|
static void
|
|
_get_program_and_map_names(
|
|
_In_ const string& path,
|
|
_Inout_ vector<section_program_map_t>& section_to_program_map,
|
|
_Inout_ vector<section_offset_to_map_t>& map_names,
|
|
uint32_t expected_map_count) noexcept(false)
|
|
{
|
|
ELFIO::elfio reader;
|
|
size_t symbols_count = 0;
|
|
|
|
if (!reader.load(path)) {
|
|
throw std::runtime_error(string("Can't process ELF file ") + path);
|
|
}
|
|
|
|
ELFIO::const_symbol_section_accessor symbols{reader, reader.sections[".symtab"]};
|
|
for (const auto& section : reader.sections) {
|
|
const string name = section->get_name();
|
|
bool found = false;
|
|
int index;
|
|
for (index = 0; index < section_to_program_map.size(); index++) {
|
|
if (section_to_program_map[index].section_name == name) {
|
|
found = true;
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
continue;
|
|
}
|
|
|
|
auto section_index = section->get_index();
|
|
bool symbol_found = false;
|
|
|
|
for (int i = 0; i < symbols.get_symbols_num(); i++) {
|
|
string symbol_name;
|
|
ELFIO::Elf64_Addr symbol_value{};
|
|
unsigned char symbol_bind{};
|
|
unsigned char symbol_type{};
|
|
ELFIO::Elf_Half symbol_section_index{};
|
|
unsigned char symbol_other{};
|
|
ELFIO::Elf_Xword symbol_size{};
|
|
|
|
symbols.get_symbol(
|
|
i,
|
|
symbol_name,
|
|
symbol_value,
|
|
symbol_size,
|
|
symbol_bind,
|
|
symbol_type,
|
|
symbol_section_index,
|
|
symbol_other);
|
|
|
|
if (!symbol_name.empty() && symbol_section_index == section_index && symbol_value == 0) {
|
|
symbol_found = true;
|
|
section_to_program_map[index].program_name = symbol_name;
|
|
symbols_count++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!symbol_found) {
|
|
throw std::runtime_error(string("Program name not found for section ") + name);
|
|
}
|
|
}
|
|
|
|
ELFIO::section* maps_section = reader.sections["maps"];
|
|
if (maps_section) {
|
|
ELFIO::Elf_Half map_section_index = maps_section->get_index();
|
|
|
|
for (ELFIO::Elf_Xword i = 0; i < symbols.get_symbols_num(); i++) {
|
|
string symbol_name;
|
|
ELFIO::Elf64_Addr symbol_value{};
|
|
unsigned char symbol_bind{};
|
|
unsigned char symbol_type{};
|
|
ELFIO::Elf_Half symbol_section_index{};
|
|
unsigned char symbol_other{};
|
|
ELFIO::Elf_Xword symbol_size{};
|
|
|
|
symbols.get_symbol(
|
|
i,
|
|
symbol_name,
|
|
symbol_value,
|
|
symbol_size,
|
|
symbol_bind,
|
|
symbol_type,
|
|
symbol_section_index,
|
|
symbol_other);
|
|
|
|
if (symbol_section_index == map_section_index) {
|
|
map_names.emplace_back(symbol_value, symbol_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expected_map_count != map_names.size()) {
|
|
throw std::runtime_error(string("Map name not found for some maps."));
|
|
}
|
|
|
|
if (symbols_count != section_to_program_map.size()) {
|
|
throw std::runtime_error(string("Program name not found for some sections."));
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_ ebpf_result_t
|
|
load_byte_code(
|
|
_In_z_ const char* filename,
|
|
_In_opt_z_ const char* section_name,
|
|
_In_ const ebpf_verifier_options_t* verifier_options,
|
|
_In_z_ const char* pin_root_path,
|
|
_Inout_ std::vector<ebpf_program_t*>& programs,
|
|
_Inout_ std::vector<ebpf_map_t*>& maps,
|
|
_Outptr_result_maybenull_z_ const char** error_message) noexcept
|
|
{
|
|
ebpf_result_t result = EBPF_SUCCESS;
|
|
ebpf_program_t* program = nullptr;
|
|
ebpf_map_t* map = nullptr;
|
|
vector<section_program_map_t> section_to_program_map;
|
|
vector<section_offset_to_map_t> map_names;
|
|
*error_message = nullptr;
|
|
|
|
try {
|
|
const ebpf_platform_t* platform = &g_ebpf_platform_windows;
|
|
std::string file_name(filename);
|
|
std::string section_name_string;
|
|
if (section_name != nullptr) {
|
|
section_name_string = std::string(section_name);
|
|
}
|
|
|
|
auto raw_programs = read_elf(file_name, section_name_string, verifier_options, platform);
|
|
if (raw_programs.size() == 0) {
|
|
result = EBPF_ELF_PARSING_FAILED;
|
|
goto Exit;
|
|
}
|
|
|
|
// read_elf() also returns a section with name ".text".
|
|
// Remove that section from the list of programs returned unless it's the only one.
|
|
for (int i = 0; i < raw_programs.size(); i++) {
|
|
if (raw_programs[i].section == ".text" && raw_programs.size() > 1) {
|
|
raw_programs.erase(raw_programs.begin() + i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// For each program/section parsed, program type should be same.
|
|
if (get_global_program_type() == nullptr) {
|
|
ebpf_program_type_t program_type = *(const GUID*)raw_programs[0].info.type.platform_specific_data;
|
|
for (auto& raw_program : raw_programs) {
|
|
if (raw_program.info.type.platform_specific_data == 0) {
|
|
result = EBPF_ELF_PARSING_FAILED;
|
|
goto Exit;
|
|
}
|
|
|
|
ebpf_program_type_t type = *(const GUID*)raw_program.info.type.platform_specific_data;
|
|
if (!IsEqualGUID(program_type, type)) {
|
|
result = EBPF_ELF_PARSING_FAILED;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& raw_program : raw_programs) {
|
|
program = (ebpf_program_t*)ebpf_allocate(sizeof(ebpf_program_t));
|
|
if (program == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
program->handle = ebpf_handle_invalid;
|
|
program->program_type = *(const GUID*)raw_program.info.type.platform_specific_data;
|
|
program->section_name = ebpf_duplicate_string(raw_program.section.c_str());
|
|
if (program->section_name == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
size_t instruction_count = raw_program.prog.size();
|
|
if (instruction_count > UINT32_MAX) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
size_t ebpf_bytes = instruction_count * sizeof(ebpf_inst);
|
|
program->instructions = (ebpf_inst*)ebpf_allocate(ebpf_bytes);
|
|
if (program->instructions == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
// Update attach type for the program.
|
|
if (get_global_program_type() != nullptr) {
|
|
const ebpf_attach_type_t* attach_type = get_global_attach_type();
|
|
if (attach_type != nullptr) {
|
|
program->attach_type = *attach_type;
|
|
}
|
|
} else {
|
|
program->attach_type = *(get_attach_type_windows(std::string(program->section_name)));
|
|
}
|
|
|
|
int i = 0;
|
|
for (ebpf_inst instruction : raw_program.prog) {
|
|
program->instructions[i++] = instruction;
|
|
}
|
|
program->instruction_count = (uint32_t)instruction_count;
|
|
programs.emplace_back(program);
|
|
program = nullptr;
|
|
}
|
|
|
|
// Get program names for each section.
|
|
for (auto& iterator : programs) {
|
|
section_to_program_map.emplace_back(iterator->section_name, std::string());
|
|
}
|
|
|
|
_get_program_and_map_names(file_name, section_to_program_map, map_names, (uint32_t)get_map_descriptor_size());
|
|
|
|
auto map_descriptors = get_all_map_descriptors();
|
|
for (const auto& descriptor : map_descriptors) {
|
|
bool found = false;
|
|
int index;
|
|
for (index = 0; index < map_names.size(); index++) {
|
|
if (descriptor.section_offset == map_names[index].section_offset) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
result = EBPF_ELF_PARSING_FAILED;
|
|
goto Exit;
|
|
}
|
|
|
|
// Currently only PIN_NONE and PIN_GLOBAL_NS pinning options are supported.
|
|
if (descriptor.pinning != PIN_NONE && descriptor.pinning != PIN_GLOBAL_NS) {
|
|
result = EBPF_INVALID_ARGUMENT;
|
|
goto Exit;
|
|
}
|
|
|
|
map = (ebpf_map_t*)ebpf_allocate(sizeof(ebpf_map_t));
|
|
if (map == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
initialize_map(map, descriptor);
|
|
map->name = ebpf_duplicate_string(map_names[index].map_name.c_str());
|
|
if (map->name == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
if (descriptor.pinning == PIN_GLOBAL_NS) {
|
|
char buffer[EBPF_MAX_PIN_PATH_LENGTH];
|
|
int len = snprintf(buffer, EBPF_MAX_PIN_PATH_LENGTH, "%s/%s", pin_root_path, map->name);
|
|
if (len < 0 || len >= EBPF_MAX_PIN_PATH_LENGTH) {
|
|
result = EBPF_INVALID_ARGUMENT;
|
|
goto Exit;
|
|
}
|
|
map->pin_path = ebpf_duplicate_string(buffer);
|
|
if (map->pin_path == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
}
|
|
maps.emplace_back(map);
|
|
map = nullptr;
|
|
}
|
|
|
|
int index = 0;
|
|
for (auto& iterator : programs) {
|
|
iterator->program_name = ebpf_duplicate_string(section_to_program_map[index].program_name.c_str());
|
|
if (iterator->program_name == nullptr) {
|
|
result = EBPF_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
index++;
|
|
}
|
|
} catch (std::runtime_error& err) {
|
|
auto message = err.what();
|
|
auto message_length = strlen(message) + 1;
|
|
char* error = reinterpret_cast<char*>(ebpf_allocate(message_length + 1));
|
|
if (error) {
|
|
strcpy_s(error, message_length, message);
|
|
}
|
|
*error_message = error;
|
|
result = EBPF_INVALID_ARGUMENT;
|
|
} catch (const std::bad_alloc&) {
|
|
result = EBPF_NO_MEMORY;
|
|
} catch (...) {
|
|
result = EBPF_FAILED;
|
|
}
|
|
|
|
Exit:
|
|
if (result != EBPF_SUCCESS) {
|
|
if (program != nullptr) {
|
|
clean_up_ebpf_program(program);
|
|
program = nullptr;
|
|
}
|
|
if (map != nullptr) {
|
|
clean_up_ebpf_map(map);
|
|
map = nullptr;
|
|
}
|
|
|
|
clean_up_ebpf_programs(programs);
|
|
clean_up_ebpf_maps(maps);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
_ebpf_add_stat(_Inout_ ebpf_section_info_t* info, std::string key, int value) noexcept(false)
|
|
{
|
|
ebpf_stat_t* stat = (ebpf_stat_t*)ebpf_allocate(sizeof(*stat));
|
|
if (stat == nullptr) {
|
|
throw std::runtime_error("Out of memory");
|
|
}
|
|
stat->key = ebpf_duplicate_string(key.c_str());
|
|
if (stat->key == nullptr) {
|
|
ebpf_free(stat);
|
|
throw std::runtime_error("Out of memory");
|
|
}
|
|
stat->value = value;
|
|
stat->next = info->stats;
|
|
info->stats = stat;
|
|
}
|
|
|
|
uint32_t
|
|
ebpf_api_elf_enumerate_sections(
|
|
_In_z_ const char* file,
|
|
_In_opt_z_ const char* section,
|
|
bool verbose,
|
|
_Outptr_result_maybenull_ ebpf_section_info_t** infos,
|
|
_Outptr_result_maybenull_z_ const char** error_message) noexcept
|
|
{
|
|
ebpf_verifier_options_t verifier_options{false, false, false, false, true};
|
|
const ebpf_platform_t* platform = &g_ebpf_platform_windows;
|
|
std::ostringstream str;
|
|
struct _thread_local_storage_cache tls_cache;
|
|
|
|
*infos = nullptr;
|
|
*error_message = nullptr;
|
|
|
|
try {
|
|
auto raw_programs = read_elf(file, section ? std::string(section) : std::string(), &verifier_options, platform);
|
|
for (const auto& raw_program : raw_programs) {
|
|
ebpf_section_info_t* info = (ebpf_section_info_t*)ebpf_allocate(sizeof(*info));
|
|
if (info == nullptr) {
|
|
throw std::runtime_error("Out of memory");
|
|
}
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
if (verbose) {
|
|
std::variant<InstructionSeq, std::string> programOrError = unmarshal(raw_program);
|
|
if (std::holds_alternative<std::string>(programOrError)) {
|
|
std::cout << "parse failure: " << std::get<std::string>(programOrError) << "\n";
|
|
ebpf_free(info);
|
|
return 1;
|
|
}
|
|
auto& program = std::get<InstructionSeq>(programOrError);
|
|
cfg_t controlFlowGraph = prepare_cfg(program, raw_program.info, true);
|
|
std::map<std::string, int> stats = collect_stats(controlFlowGraph);
|
|
for (auto it = stats.rbegin(); it != stats.rend(); ++it) {
|
|
_ebpf_add_stat(info, it->first, it->second);
|
|
}
|
|
_ebpf_add_stat(info, "Instructions", (int)raw_program.prog.size());
|
|
}
|
|
|
|
info->section_name = ebpf_duplicate_string(raw_program.section.c_str());
|
|
info->program_type_name = ebpf_duplicate_string(raw_program.info.type.name.c_str());
|
|
|
|
std::vector<uint8_t> raw_data = convert_ebpf_program_to_bytes(raw_program.prog);
|
|
info->raw_data_size = raw_data.size();
|
|
info->raw_data = (char*)ebpf_allocate(info->raw_data_size);
|
|
if (info->raw_data == nullptr || info->section_name == nullptr || info->program_type_name == nullptr) {
|
|
ebpf_free((void*)info->section_name);
|
|
ebpf_free((void*)info->program_type_name);
|
|
ebpf_free((void*)info->raw_data);
|
|
ebpf_free(info);
|
|
throw std::runtime_error("Out of memory");
|
|
}
|
|
memcpy(info->raw_data, raw_data.data(), info->raw_data_size);
|
|
|
|
info->next = *infos;
|
|
*infos = info;
|
|
}
|
|
} catch (std::runtime_error e) {
|
|
str << "error: " << e.what();
|
|
*error_message = allocate_string(str.str());
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t
|
|
ebpf_api_elf_disassemble_section(
|
|
_In_z_ const char* file,
|
|
_In_z_ const char* section,
|
|
_Outptr_result_maybenull_z_ const char** disassembly,
|
|
_Outptr_result_maybenull_z_ const char** error_message)
|
|
{
|
|
ebpf_verifier_options_t verifier_options = ebpf_verifier_default_options;
|
|
const ebpf_platform_t* platform = &g_ebpf_platform_windows;
|
|
std::ostringstream error;
|
|
std::ostringstream output;
|
|
struct _thread_local_storage_cache tls_cache;
|
|
|
|
*disassembly = nullptr;
|
|
*error_message = nullptr;
|
|
|
|
try {
|
|
auto raw_programs = read_elf(file, section, &verifier_options, platform);
|
|
raw_program raw_program = raw_programs.back();
|
|
std::variant<InstructionSeq, std::string> programOrError = unmarshal(raw_program);
|
|
if (std::holds_alternative<std::string>(programOrError)) {
|
|
error << "parse failure: " << std::get<std::string>(programOrError);
|
|
*error_message = allocate_string(error.str());
|
|
return 1;
|
|
}
|
|
auto& program = std::get<InstructionSeq>(programOrError);
|
|
print(program, output, {}, true);
|
|
*disassembly = allocate_string(output.str());
|
|
if (!*disassembly) {
|
|
return 1;
|
|
}
|
|
} catch (std::runtime_error e) {
|
|
error << "error: " << e.what();
|
|
*error_message = allocate_string(error.str());
|
|
return 1;
|
|
} catch (std::exception ex) {
|
|
error << "Failed to load eBPF program from " << file;
|
|
*error_message = allocate_string(error.str());
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t
|
|
_ebpf_api_elf_verify_section_from_stream(
|
|
std::istream& stream,
|
|
const char* stream_name,
|
|
const char* section,
|
|
bool verbose,
|
|
const char** report,
|
|
const char** error_message,
|
|
ebpf_api_verifier_stats_t* stats) noexcept
|
|
{
|
|
std::ostringstream error;
|
|
std::ostringstream output;
|
|
struct _thread_local_storage_cache tls_cache;
|
|
*report = nullptr;
|
|
*error_message = nullptr;
|
|
|
|
try {
|
|
const ebpf_platform_t* platform = &g_ebpf_platform_windows;
|
|
ebpf_verifier_options_t verifier_options = ebpf_verifier_default_options;
|
|
verifier_options.check_termination = true;
|
|
verifier_options.print_invariants = verbose;
|
|
verifier_options.print_failures = true;
|
|
verifier_options.mock_map_fds = true;
|
|
verifier_options.print_line_info = true;
|
|
if (!stream) {
|
|
throw std::runtime_error(std::string("No such file or directory opening ") + stream_name);
|
|
}
|
|
auto raw_programs = read_elf(stream, stream_name, section, &verifier_options, platform);
|
|
raw_program raw_program = raw_programs.back();
|
|
std::variant<InstructionSeq, std::string> programOrError = unmarshal(raw_program);
|
|
if (std::holds_alternative<std::string>(programOrError)) {
|
|
error << "parse failure: " << std::get<std::string>(programOrError);
|
|
*error_message = allocate_string(error.str());
|
|
return 1;
|
|
}
|
|
auto& program = std::get<InstructionSeq>(programOrError);
|
|
|
|
verifier_options.no_simplify = true;
|
|
bool res =
|
|
ebpf_verify_program(output, program, raw_program.info, &verifier_options, (ebpf_verifier_stats_t*)stats);
|
|
if (!res) {
|
|
error << "Verification failed";
|
|
*error_message = allocate_string(error.str());
|
|
*report = allocate_string(output.str());
|
|
return 1;
|
|
}
|
|
|
|
output << "Verification succeeded";
|
|
*report = allocate_string(output.str());
|
|
if (!*report) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
} catch (std::runtime_error e) {
|
|
error << "error: " << e.what();
|
|
*error_message = allocate_string(error.str());
|
|
return 1;
|
|
} catch (std::exception ex) {
|
|
error << "Failed to load eBPF program from " << stream_name;
|
|
*error_message = allocate_string(error.str());
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t
|
|
_load_file_to_memory(
|
|
_In_ const std::string& path,
|
|
_Out_ std::string& data,
|
|
_Outptr_result_maybenull_z_ const char** error_message) noexcept
|
|
{
|
|
data = "";
|
|
struct stat st;
|
|
if (stat(path.c_str(), &st)) {
|
|
*error_message = allocate_string(std::string("error: No such file or directory opening ") + path);
|
|
return 1;
|
|
}
|
|
if (std::ifstream stream{path, std::ios::in | std::ios::binary}) {
|
|
data.resize(st.st_size);
|
|
if (stream.read(data.data(), data.size())) {
|
|
*error_message = nullptr;
|
|
return 0;
|
|
}
|
|
}
|
|
*error_message = allocate_string(std::string("error: Failed to read file: ") + path);
|
|
return 1;
|
|
}
|
|
|
|
static _Success_(return == 0) uint32_t _verify_section_from_string(
|
|
std::string data,
|
|
_In_z_ const char* name,
|
|
_In_z_ const char* section,
|
|
_In_opt_ const ebpf_program_type_t* program_type,
|
|
bool verbose,
|
|
_Outptr_result_maybenull_z_ const char** report,
|
|
_Outptr_result_maybenull_z_ const char** error_message,
|
|
_Out_opt_ ebpf_api_verifier_stats_t* stats) noexcept
|
|
{
|
|
*error_message = nullptr;
|
|
*report = nullptr;
|
|
|
|
if (!ElfCheckElf(
|
|
data.size(),
|
|
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(data.data())),
|
|
static_cast<uint32_t>(data.size()))) {
|
|
|
|
*error_message =
|
|
allocate_string(std::string("error: ELF file ") + name + " is malformed: " + _elf_everparse_error);
|
|
return 1;
|
|
}
|
|
|
|
auto stream = std::stringstream(data);
|
|
struct _thread_local_storage_cache tls_cache;
|
|
set_global_program_and_attach_type(program_type, nullptr);
|
|
set_verification_in_progress(true);
|
|
return _ebpf_api_elf_verify_section_from_stream(stream, name, section, verbose, report, error_message, stats);
|
|
}
|
|
|
|
_Success_(return == 0) uint32_t ebpf_api_elf_verify_section_from_file(
|
|
_In_z_ const char* file,
|
|
_In_z_ const char* section,
|
|
_In_opt_ const ebpf_program_type_t* program_type,
|
|
bool verbose,
|
|
_Outptr_result_maybenull_z_ const char** report,
|
|
_Outptr_result_maybenull_z_ const char** error_message,
|
|
_Out_opt_ ebpf_api_verifier_stats_t* stats)
|
|
{
|
|
*error_message = nullptr;
|
|
*report = nullptr;
|
|
std::string data;
|
|
uint32_t error = _load_file_to_memory(file, data, error_message);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
return _verify_section_from_string(data, file, section, program_type, verbose, report, error_message, stats);
|
|
}
|
|
|
|
_Success_(return == 0) uint32_t ebpf_api_elf_verify_section_from_memory(
|
|
_In_reads_(data_length) const char* data,
|
|
size_t data_length,
|
|
_In_z_ const char* section,
|
|
_In_opt_ const ebpf_program_type_t* program_type,
|
|
bool verbose,
|
|
_Outptr_result_maybenull_z_ const char** report,
|
|
_Outptr_result_maybenull_z_ const char** error_message,
|
|
_Out_opt_ ebpf_api_verifier_stats_t* stats)
|
|
{
|
|
return _verify_section_from_string(
|
|
std::string(data, data_length), "memory", section, program_type, verbose, report, error_message, stats);
|
|
}
|