• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vbpf / ebpf-verifier / 13778556390

11 Mar 2025 01:47AM UTC coverage: 88.161% (-0.02%) from 88.176%
13778556390

Pull #846

github

web-flow
Merge ec44477a0 into d4d0336ac
Pull Request #846: Refactor asm_files.cpp

198 of 230 new or added lines in 2 files covered. (86.09%)

3 existing lines in 1 file now uncovered.

8556 of 9705 relevant lines covered (88.16%)

7078063.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.02
/src/asm_files.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3
#include <iostream>
4
#include <map>
5
#include <set>
6
#include <string>
7
#include <sys/stat.h>
8
#include <vector>
9

10
#include "elfio/elfio.hpp"
11
#include "libbtf/btf_json.h"
12
#include "libbtf/btf_map.h"
13
#include "libbtf/btf_parse.h"
14

15
#include "asm_files.hpp"
16
#include "crab_utils/num_safety.hpp"
17
#include "platform.hpp"
18

19
using std::string;
20

21
template <typename T>
22
    requires std::is_trivially_copyable_v<T>
23
static std::vector<T> vector_of(const char* data, const ELFIO::Elf_Xword size) {
7,604✔
24
    if (size % sizeof(T) != 0 || size > std::numeric_limits<uint32_t>::max() || !data) {
7,604✔
25
        throw UnmarshalError("Invalid argument to vector_of");
×
26
    }
27
    return {reinterpret_cast<const T*>(data), reinterpret_cast<const T*>(data + size)};
15,208✔
28
}
29

30
template <typename T>
31
    requires std::is_trivially_copyable_v<T>
32
static std::vector<T> vector_of(const ELFIO::section& sec) {
488✔
33
    return vector_of<T>(sec.get_data(), sec.get_size());
488✔
34
}
35

36
int create_map_crab(const EbpfMapType& map_type, const uint32_t key_size, const uint32_t value_size,
5,800✔
37
                    const uint32_t max_entries, ebpf_verifier_options_t) {
38
    const EquivalenceKey equiv{map_type.value_type, key_size, value_size, map_type.is_array ? max_entries : 0};
5,800✔
39
    if (!thread_local_program_info->cache.contains(equiv)) {
5,800✔
40
        // +1 so 0 is the null FD
41
        thread_local_program_info->cache[equiv] = gsl::narrow<int>(thread_local_program_info->cache.size()) + 1;
5,022✔
42
    }
43
    return thread_local_program_info->cache.at(equiv);
8,700✔
44
}
45

46
EbpfMapDescriptor* find_map_descriptor(const int map_fd) {
17,354✔
47
    for (EbpfMapDescriptor& map : thread_local_program_info->map_descriptors) {
152,376✔
48
        if (map.original_fd == map_fd) {
152,374✔
49
            return &map;
17,352✔
50
        }
51
    }
52
    return nullptr;
1✔
53
}
54

55
// Maps sections are identified as any section called "maps", or matching "maps/<map-name>".
56
static bool is_map_section(const string& name) {
38,954✔
57
    const string maps_prefix = "maps/";
38,954✔
58
    return name == "maps" || (name.length() > 5 && name.compare(0, maps_prefix.length(), maps_prefix) == 0);
77,908✔
59
}
38,954✔
60

61
static std::tuple<string, ELFIO::Elf_Half>
62
get_symbol_name_and_section_index(const ELFIO::const_symbol_section_accessor& symbols, const ELFIO::Elf_Xword index) {
30,284,630✔
63
    string symbol_name;
30,284,630✔
64
    ELFIO::Elf64_Addr value{};
30,284,630✔
65
    ELFIO::Elf_Xword size{};
30,284,630✔
66
    unsigned char bind{};
30,284,630✔
67
    unsigned char type{};
30,284,630✔
68
    ELFIO::Elf_Half section_index{};
30,284,630✔
69
    unsigned char other{};
30,284,630✔
70
    symbols.get_symbol(index, symbol_name, value, size, bind, type, section_index, other);
30,284,630✔
71
    return {symbol_name, section_index};
60,569,260✔
72
}
30,284,630✔
73

74
static std::tuple<ELFIO::Elf64_Addr, unsigned char> get_value(const ELFIO::const_symbol_section_accessor& symbols,
415,160✔
75
                                                              const ELFIO::Elf_Xword index) {
76
    string symbol_name;
415,160✔
77
    ELFIO::Elf64_Addr value{};
415,160✔
78
    ELFIO::Elf_Xword size{};
415,160✔
79
    unsigned char bind{};
415,160✔
80
    unsigned char type{};
415,160✔
81
    ELFIO::Elf_Half section_index{};
415,160✔
82
    unsigned char other{};
415,160✔
83
    symbols.get_symbol(index, symbol_name, value, size, bind, type, section_index, other);
415,160✔
84
    return {value, type};
622,740✔
85
}
415,160✔
86

87
// parse_maps_sections processes all maps sections in the provided ELF file by calling the platform-specific maps'
88
// parser. The section index of each maps section is inserted into map_section_indices.
89
static size_t parse_map_sections(const ebpf_verifier_options_t& options, const ebpf_platform_t* platform,
564✔
90
                                 const ELFIO::elfio& reader, std::vector<EbpfMapDescriptor>& map_descriptors,
91
                                 std::set<ELFIO::Elf_Half>& map_section_indices,
92
                                 const ELFIO::const_symbol_section_accessor& symbols) {
93
    size_t map_record_size = platform->map_record_size;
564✔
94
    for (ELFIO::Elf_Half i = 0; i < reader.sections.size(); ++i) {
20,532✔
95
        const auto s = reader.sections[i];
19,968✔
96
        if (!is_map_section(s->get_name())) {
19,968✔
97
            continue;
19,402✔
98
        }
99

100
        // Count the number of symbols that point into this maps section.
101
        int map_count = 0;
283✔
102
        for (ELFIO::Elf_Xword index = 0; index < symbols.get_symbols_num(); index++) {
484,974✔
103
            auto [symbol_name, section_index] = get_symbol_name_and_section_index(symbols, index);
484,408✔
104
            if (section_index == i && !symbol_name.empty()) {
484,408✔
105
                map_count++;
5,800✔
106
            }
107
        }
484,408✔
108

109
        if (map_count > 0) {
566✔
110
            map_record_size = s->get_size() / map_count;
566✔
111
            if (s->get_data() == nullptr || map_record_size == 0) {
566✔
112
                throw UnmarshalError("bad maps section");
×
113
            }
114
            if (s->get_size() % map_record_size != 0) {
566✔
115
                throw UnmarshalError("bad maps section size");
×
116
            }
117
            platform->parse_maps_section(map_descriptors, s->get_data(), map_record_size, map_count, platform, options);
566✔
118
        }
119
        map_section_indices.insert(s->get_index());
566✔
120
    }
121
    platform->resolve_inner_map_references(map_descriptors);
564✔
122
    return map_record_size;
564✔
123
}
124

125
std::vector<raw_program> read_elf(const string& path, const string& desired_section,
756✔
126
                                  const ebpf_verifier_options_t& options, const ebpf_platform_t* platform) {
127
    if (std::ifstream stream{path, std::ios::in | std::ios::binary}) {
756✔
128
        return read_elf(stream, path, desired_section, options, platform);
1,502✔
129
    }
756✔
130
    struct stat st;
1✔
131
    if (stat(path.c_str(), &st)) {
2✔
132
        throw UnmarshalError(string(strerror(errno)) + " opening " + path);
6✔
133
    }
134
    throw UnmarshalError("Can't process ELF file " + path);
×
135
}
136

137
static std::tuple<string, ELFIO::Elf_Xword>
138
get_program_name_and_size(const ELFIO::section& sec, const ELFIO::Elf_Xword start,
7,116✔
139
                          const ELFIO::const_symbol_section_accessor& symbols) {
140
    const ELFIO::Elf_Xword symbol_count = symbols.get_symbols_num();
7,116✔
141
    const ELFIO::Elf_Half section_index = sec.get_index();
7,116✔
142
    string program_name = sec.get_name();
7,116✔
143
    ELFIO::Elf_Xword size = sec.get_size() - start;
7,116✔
144
    for (ELFIO::Elf_Xword index = 0; index < symbol_count; index++) {
29,619,048✔
145
        auto [symbol_name, symbol_section_index] = get_symbol_name_and_section_index(symbols, index);
29,611,932✔
146
        if (symbol_section_index == section_index && !symbol_name.empty()) {
29,611,932✔
147
            auto [relocation_offset, relocation_type] = get_value(symbols, index);
258,322✔
148
            if (relocation_type != ELFIO::STT_FUNC) {
258,322✔
149
                continue;
255,140✔
150
            }
151
            if (relocation_offset == start) {
3,182✔
152
                // We found the program name for this program.
153
                program_name = symbol_name;
4,561✔
154
            } else if (relocation_offset > start && relocation_offset < start + size) {
212✔
155
                // We found another program that follows, so truncate the size of this program.
156
                size = relocation_offset - start;
76✔
157
            }
158
        }
159
    }
29,611,932✔
160
    return {program_name, size};
14,232✔
161
}
7,116✔
162

163
static void relocate_map(ebpf_inst& reloc_inst, const std::string& symbol_name,
156,922✔
164
                         const std::variant<size_t, std::map<std::string, size_t>>& map_record_size_or_map_offsets,
165
                         const std::vector<EbpfMapDescriptor>& map_descriptors, const ELFIO::Elf_Word index,
166
                         const ELFIO::const_symbol_section_accessor& symbols) {
167
    reloc_inst.src = INST_LD_MODE_MAP_FD;
156,922✔
168

169
    // Relocation value is an offset into the "maps" or ".maps" section.
170
    size_t reloc_value{};
156,922✔
171
    if (map_record_size_or_map_offsets.index() == 0) {
156,922✔
172
        // The older maps section format uses a single map_record_size value, so we can
173
        // calculate the map descriptor index directly.
174
        auto [relocation_offset, relocation_type] = get_value(symbols, index);
156,838✔
175
        reloc_value = relocation_offset / std::get<0>(map_record_size_or_map_offsets);
156,838✔
176
    } else {
177
        // The newer .maps section format uses a variable-length map descriptor array,
178
        // so we need to look up the map descriptor index in a map.
179
        const auto& map_descriptors_offsets = std::get<1>(map_record_size_or_map_offsets);
84✔
180
        const auto it = map_descriptors_offsets.find(symbol_name);
84✔
181
        if (it == map_descriptors_offsets.end()) {
84✔
UNCOV
182
            throw UnmarshalError("Map descriptor not found for symbol " + symbol_name);
×
183
        }
184
        reloc_value = it->second;
84✔
185
    }
186
    if (reloc_value >= map_descriptors.size()) {
156,922✔
187
        throw UnmarshalError("Bad reloc value (" + std::to_string(reloc_value) + "). " +
×
188
                             "Make sure to compile with -O2.");
×
189
    }
190
    reloc_inst.imm = map_descriptors.at(reloc_value).original_fd;
156,922✔
191
}
156,922✔
192

193
static void
194
relocate_global_variable(ebpf_inst& reloc_inst, ebpf_inst& next_reloc_inst, const std::string& symbol_name,
8✔
195
                         const std::vector<EbpfMapDescriptor>& map_descriptors,
196
                         const std::variant<size_t, std::map<std::string, size_t>>& map_record_size_or_map_offsets) {
197

198
    // Copy the immediate value to the next instruction.
199
    next_reloc_inst.imm = reloc_inst.imm;
8✔
200
    reloc_inst.src = INST_LD_MODE_MAP_VALUE;
8✔
201

202
    const auto& map_descriptors_offsets = std::get<1>(map_record_size_or_map_offsets);
8✔
203
    const auto it = map_descriptors_offsets.find(symbol_name);
8✔
204
    if (it == map_descriptors_offsets.end()) {
8✔
UNCOV
205
        throw UnmarshalError("Map descriptor not found for symbol " + symbol_name);
×
206
    }
207
    if (it->second >= map_descriptors.size()) {
8✔
NEW
208
        throw UnmarshalError("Bad reloc value (" + std::to_string(it->second) + "). " +
×
UNCOV
209
                             "Make sure to compile with -O2.");
×
210
    }
211
    reloc_inst.imm = map_descriptors.at(it->second).original_fd;
8✔
212
}
8✔
213

214
// Structure used to keep track of subprogram relocation data until any subprograms
215
// are loaded and can be appended to the calling program.
216
struct function_relocation {
78,548✔
217
    size_t prog_index{};              // Index of source program in std::vector of raw programs.
218
    ELFIO::Elf_Xword source_offset{}; // Instruction offset in source section of source instruction.
219
    ELFIO::Elf_Xword relocation_entry_index{};
220
    string target_function_name;
221
};
222

223
static raw_program* find_subprogram(std::vector<raw_program>& programs, const ELFIO::section& subprogram_section,
1,958✔
224
                                    const std::string& symbol_name) {
225
    // Find subprogram by name.
226
    for (auto& subprog : programs) {
186,646✔
227
        if (subprog.section_name == subprogram_section.get_name() && subprog.function_name == symbol_name) {
277,050✔
228
            return &subprog;
12✔
229
        }
230
    }
231
    return nullptr;
973✔
232
}
233

234
static std::map<std::string, size_t> parse_map_section(const libbtf::btf_type_data& btf_data,
116✔
235
                                                       std::vector<EbpfMapDescriptor>& map_descriptors) {
236
    std::map<std::string, size_t> map_offsets;
116✔
237
    for (const auto& map : parse_btf_map_section(btf_data)) {
208✔
238
        map_offsets.emplace(map.name, map_descriptors.size());
92✔
239
        map_descriptors.push_back({
92✔
240
            .original_fd = gsl::narrow_cast<int>(map.type_id),
92✔
241
            .type = map.map_type,
92✔
242
            .key_size = map.key_size,
92✔
243
            .value_size = map.value_size,
92✔
244
            .max_entries = map.max_entries,
92✔
245
            .inner_map_fd = map.inner_map_type_id != 0 ? map.inner_map_type_id : DEFAULT_MAP_FD,
92✔
246
        });
247
    }
116✔
248
    return map_offsets;
116✔
249
}
×
250

251
static std::map<int, int> map_typeid_to_fd(const std::vector<EbpfMapDescriptor>& map_descriptors) {
116✔
252
    std::map<int, int> type_id_to_fd_map;
116✔
253
    int pseudo_fd = 1;
116✔
254
    // Gather the typeids for each map and assign a pseudo-fd to each map.
255
    for (const auto& map_descriptor : map_descriptors) {
208✔
256
        if (!type_id_to_fd_map.contains(map_descriptor.original_fd)) {
92✔
257
            type_id_to_fd_map[map_descriptor.original_fd] = pseudo_fd++;
92✔
258
        }
259
    }
260
    return type_id_to_fd_map;
116✔
NEW
261
}
×
262

263
static void replace_typeids_with_pseudo_fds(std::vector<EbpfMapDescriptor>& map_descriptors) {
116✔
264
    // Prevail requires:
265
    // Map fds are sequential starting from 1.
266
    // Map fds are assigned in the order of the maps in the ELF file.
267
    const std::map<int, int> type_id_to_fd_map = map_typeid_to_fd(map_descriptors);
116✔
268
    for (auto& map_descriptor : map_descriptors) {
208✔
269
        map_descriptor.original_fd = type_id_to_fd_map.at(map_descriptor.original_fd);
92✔
270
        if (map_descriptor.inner_map_fd != DEFAULT_MAP_FD) {
92✔
271
            map_descriptor.inner_map_fd = type_id_to_fd_map.at(map_descriptor.inner_map_fd);
6✔
272
        }
273
    }
274
}
116✔
275

276
static ELFIO::const_symbol_section_accessor read_and_validate_symbol_section(const ELFIO::elfio& reader,
754✔
277
                                                                             const std::string& path) {
278
    const ELFIO::section* symbol_section = reader.sections[".symtab"];
754✔
279
    if (!symbol_section) {
754✔
280
        throw UnmarshalError("No symbol section found in ELF file " + path);
×
281
    }
282

283
    // Make sure the ELFIO library will be able to parse the symbol section correctly.
284
    const auto expected_entry_size =
377✔
285
        reader.get_class() == ELFIO::ELFCLASS32 ? sizeof(ELFIO::Elf32_Sym) : sizeof(ELFIO::Elf64_Sym);
754✔
286

287
    if (symbol_section->get_entry_size() != expected_entry_size) {
754✔
288
        throw UnmarshalError("Invalid symbol section found in ELF file " + path);
3✔
289
    }
290
    return ELFIO::const_symbol_section_accessor{reader, symbol_section};
752✔
291
}
292

293
static ELFIO::elfio load_elf(std::istream& input_stream, const std::string& path) {
754✔
294
    ELFIO::elfio reader;
754✔
295
    if (!reader.load(input_stream)) {
754✔
NEW
296
        throw UnmarshalError("Can't process ELF file " + path);
×
297
    }
298
    return reader;
754✔
NEW
299
}
×
300

NEW
301
static void dump_btf_types(const libbtf::btf_type_data& btf_data, const std::string& path) {
×
NEW
302
    std::stringstream output;
×
NEW
303
    std::cout << "Dumping BTF data for" << path << std::endl;
×
304
    // Dump the BTF data to stdout for debugging purposes.
NEW
305
    btf_data.to_json(output);
×
NEW
306
    std::cout << libbtf::pretty_print_json(output.str()) << std::endl;
×
NEW
307
    std::cout << std::endl;
×
NEW
308
}
×
309

310
static void update_line_info(std::vector<raw_program>& raw_programs, const ELFIO::section* btf_section,
186✔
311
                             const ELFIO::section* btf_ext) {
312
    auto visitor = [&raw_programs](const string& section, const uint32_t instruction_offset, const string& file_name,
1,256,823✔
313
                                   const string& source, const uint32_t line_number, const uint32_t column_number) {
314
        for (auto& program : raw_programs) {
61,377,566✔
315
            if (program.section_name == section && instruction_offset >= program.insn_off &&
60,749,352✔
316
                instruction_offset < program.insn_off + program.prog.size() * sizeof(ebpf_inst)) {
1,257,032✔
317
                const size_t inst_index = (instruction_offset - program.insn_off) / sizeof(ebpf_inst);
1,256,730✔
318
                if (inst_index >= program.prog.size()) {
1,256,730✔
NEW
319
                    throw UnmarshalError("Invalid BTF data");
×
320
                }
321
                program.info.line_info.insert_or_assign(inst_index,
628,365✔
322
                                                        btf_line_info_t{file_name, source, line_number, column_number});
1,885,095✔
323
            }
324
        }
325
    };
1,256,730✔
326

327
    libbtf::btf_parse_line_information(vector_of<std::byte>(*btf_section), vector_of<std::byte>(*btf_ext), visitor);
279✔
328

329
    // BTF doesn't include line info for every instruction, only on the first instruction per source line.
330
    for (auto& program : raw_programs) {
3,154✔
331
        for (size_t i = 1; i < program.prog.size(); i++) {
4,884,832✔
332
            // If the previous PC has line info, copy it.
333
            if (program.info.line_info[i].line_number == 0 && program.info.line_info[i - 1].line_number != 0) {
4,881,864✔
334
                program.info.line_info[i] = program.info.line_info[i - 1];
3,782,354✔
335
            }
336
        }
337
    }
338
}
186✔
339

340
struct global_data_t {
376✔
341
    std::set<ELFIO::Elf_Half> map_section_indices;
342
    std::vector<EbpfMapDescriptor> map_descriptors;
343
    std::variant<size_t, std::map<std::string, size_t>> map_record_size_or_map_offsets;
344
    std::set<ELFIO::Elf_Half> variable_section_indices;
345
};
346

347
struct params_t {
3✔
348
    const std::string& path;
349
    const ebpf_verifier_options_t& options;
350
    const ebpf_platform_t* platform;
351
    const std::string desired_section;
352
};
353

354
static global_data_t extract_global_data(const params_t& params, const ELFIO::elfio& reader,
752✔
355
                                         const ELFIO::const_symbol_section_accessor& symbols) {
356
    global_data_t global;
752✔
357
    if (std::ranges::any_of(reader.sections, [](const auto& section) { return is_map_section(section->get_name()); })) {
19,738✔
358
        global.map_record_size_or_map_offsets = parse_map_sections(
564✔
359
            params.options, params.platform, reader, global.map_descriptors, global.map_section_indices, symbols);
564✔
360
    } else if (const auto btf_section = reader.sections[".BTF"]) {
188✔
361
        const libbtf::btf_type_data btf_data = vector_of<std::byte>(*btf_section);
116✔
362
        if (params.options.verbosity_opts.dump_btf_types_json) {
116✔
NEW
363
            dump_btf_types(btf_data, params.path);
×
364
        }
365
        global.map_record_size_or_map_offsets = parse_map_section(btf_data, global.map_descriptors);
116✔
366
        replace_typeids_with_pseudo_fds(global.map_descriptors);
116✔
367

368
        if (const auto maps_section = reader.sections[".maps"]) {
116✔
369
            global.map_section_indices.insert(maps_section->get_index());
74✔
370
        }
371

372
        for (const auto section_name : {".rodata", ".data", ".bss"}) {
464✔
373
            if (const auto section = reader.sections[section_name]) {
348✔
374
                if (section->get_size() != 0) {
6✔
375
                    global.variable_section_indices.insert(section->get_index());
6✔
376
                }
377
            }
378
        }
379
    }
116✔
380
    return global;
752✔
NEW
381
}
×
382

383
class program_reader_t {
384
    const params_t& params;
385
    const ELFIO::elfio& reader;
386
    const ELFIO::const_symbol_section_accessor& symbols;
387
    const global_data_t& global;
388

389
    std::vector<function_relocation> function_relocations;
390
    std::vector<string> unresolved_symbols_errors;
391
    std::map<const raw_program*, bool> resolved_subprograms;
392

393
  public:
394
    std::vector<raw_program> raw_programs;
395

396
    program_reader_t(const params_t& params, const ELFIO::elfio& reader,
752✔
397
                     const ELFIO::const_symbol_section_accessor& symbols, const global_data_t& global)
398
        : params{params}, reader{reader}, symbols{symbols}, global{global} {}
752✔
399

400
    // Returns an error message, or empty string on success.
401
    std::string append_subprograms(raw_program& prog) {
7,128✔
402
        if (resolved_subprograms[&prog]) {
7,128✔
403
            // We've already appended any relevant subprograms.
404
            return {};
12✔
405
        }
406
        resolved_subprograms[&prog] = true;
7,116✔
407

408
        // Perform function relocations and fill in the inst.imm values of CallLocal instructions.
409
        std::map<string, ELFIO::Elf_Xword> subprogram_offsets;
7,116✔
410
        for (const auto& reloc : function_relocations) {
1,880,892✔
411
            if (reloc.prog_index >= raw_programs.size()) {
1,875,722✔
412
                continue;
1,873,764✔
413
            }
414
            if (raw_programs[reloc.prog_index].function_name != prog.function_name) {
1,875,722✔
415
                continue;
1,873,752✔
416
            }
417

418
            // Check whether we already appended the target program, and append it if not.
419
            if (!subprogram_offsets.contains(reloc.target_function_name)) {
1,970✔
420
                subprogram_offsets[reloc.target_function_name] = prog.prog.size();
1,958✔
421

422
                const auto [symbol_name, section_index] =
1,958✔
423
                    get_symbol_name_and_section_index(symbols, reloc.relocation_entry_index);
1,958✔
424
                if (section_index >= reader.sections.size()) {
1,958✔
NEW
425
                    throw UnmarshalError("Invalid section index " + std::to_string(section_index) +
×
NEW
426
                                         " at source offset " + std::to_string(reloc.source_offset));
×
427
                }
428
                const ELFIO::section& subprogram_section = *reader.sections[section_index];
1,958✔
429

430
                if (const auto subprogram = find_subprogram(raw_programs, subprogram_section, symbol_name)) {
1,958✔
431
                    // Make sure subprogram has already had any subprograms of its own appended.
432
                    std::string error = append_subprograms(*subprogram);
12✔
433
                    if (!error.empty()) {
12✔
NEW
434
                        return error;
×
435
                    }
436

437
                    // Append subprogram to program.
438
                    prog.prog.insert(prog.prog.end(), subprogram->prog.begin(), subprogram->prog.end());
12✔
439
                    for (size_t i = 0; i < subprogram->info.line_info.size(); i++) {
216✔
440
                        prog.info.line_info[prog.info.line_info.size()] = subprogram->info.line_info[i];
204✔
441
                    }
442
                } else {
12✔
443
                    // The program will be invalid, but continue rather than throwing an exception
444
                    // since we might be verifying a different program in the file.
445
                    return std::string("Subprogram '" + symbol_name + "' not found in section '" +
4,865✔
446
                                       subprogram_section.get_name() + "'");
5,838✔
447
                }
448
            }
1,958✔
449

450
            // Fill in the PC offset into the imm field of the CallLocal instruction.
451
            const int64_t target_offset = gsl::narrow_cast<int64_t>(subprogram_offsets[reloc.target_function_name]);
24✔
452
            const auto offset_diff = target_offset - gsl::narrow<int64_t>(reloc.source_offset) - 1;
24✔
453
            if (offset_diff < std::numeric_limits<int32_t>::min() ||
36✔
454
                offset_diff > std::numeric_limits<int32_t>::max()) {
12✔
NEW
455
                throw UnmarshalError("Offset difference out of int32_t range for instruction at source offset " +
×
NEW
456
                                     std::to_string(reloc.source_offset));
×
457
            }
458
            prog.prog[reloc.source_offset].imm = gsl::narrow_cast<int32_t>(offset_diff);
24✔
459
        }
460
        return {};
5,170✔
461
    }
7,116✔
462

463
    void try_reloc(const string& section_name, std::vector<ebpf_inst>& inst_seq, const size_t location,
186,332✔
464
                   const ELFIO::Elf_Word index) {
465
        if (location >= inst_seq.size()) {
186,332✔
NEW
466
            throw UnmarshalError("Invalid relocation data");
×
467
        }
468

469
        ebpf_inst& reloc_inst = inst_seq[location];
186,332✔
470

471
        const auto [symbol_name, symbol_section_index] = get_symbol_name_and_section_index(symbols, index);
186,332✔
472

473
        // Queue up relocation for function symbols.
474
        if (reloc_inst.opcode == INST_OP_CALL && reloc_inst.src == INST_CALL_LOCAL) {
186,332✔
475
            const function_relocation fr{.prog_index = raw_programs.size(),
29,402✔
476
                                         .source_offset = location,
477
                                         .relocation_entry_index = index,
14,701✔
478
                                         .target_function_name = symbol_name};
29,402✔
479
            function_relocations.push_back(fr);
29,402✔
480
            return;
29,402✔
481
        }
29,402✔
482

483
        // Verify that this is a map or global variable relocation.
484
        if ((reloc_inst.opcode & INST_CLS_MASK) != INST_CLS_LD) {
156,930✔
NEW
485
            throw UnmarshalError("Illegal operation on symbol " + symbol_name + " at location " +
×
NEW
486
                                 std::to_string(location));
×
487
        }
488

489
        // Perform relocation for symbols located in the maps section.
490
        if (global.map_section_indices.contains(symbol_section_index)) {
156,930✔
491
            relocate_map(reloc_inst, symbol_name, global.map_record_size_or_map_offsets, global.map_descriptors, index,
156,922✔
492
                         symbols);
493
            return;
78,461✔
494
        }
495

496
        if (global.variable_section_indices.contains(symbol_section_index)) {
8✔
497
            // Load instructions are two instructions long, so we need to check the next instruction.
498
            if (inst_seq.size() <= location + 1) {
8✔
NEW
499
                throw UnmarshalError("Invalid relocation data");
×
500
            }
501
            ebpf_inst& next_reloc_inst = inst_seq[location + 1];
8✔
502
            relocate_global_variable(reloc_inst, next_reloc_inst, reader.sections[symbol_section_index]->get_name(),
8✔
503
                                     global.map_descriptors, global.map_record_size_or_map_offsets);
8✔
504
            return;
8✔
505
        }
506

NEW
507
        unresolved_symbols_errors.push_back("Unresolved external symbol " + symbol_name + " in section " +
×
NEW
508
                                            section_name + " at location " + std::to_string(location));
×
509
    }
186,332✔
510

511
    void update_relocations(std::vector<ebpf_inst>& inst_seq, const string& section_name,
7,116✔
512
                            const ELFIO::Elf_Xword program_offset, const size_t program_size) {
513
        auto prelocs = reader.sections[".rel" + section_name];
7,116✔
514
        if (!prelocs) {
7,116✔
515
            prelocs = reader.sections[".rela" + section_name];
1,070✔
516
        }
517
        if (!prelocs) {
4,093✔
518
            return;
440✔
519
        }
520
        if (!prelocs->get_data()) {
6,676✔
NEW
521
            throw UnmarshalError("Malformed relocation data");
×
522
        }
523
        const ELFIO::const_relocation_section_accessor reloc{reader, prelocs};
6,676✔
524
        for (ELFIO::Elf_Xword i = 0; i < reloc.get_entries_num(); i++) {
193,060✔
525
            ELFIO::Elf64_Addr offset{};
186,384✔
526
            ELFIO::Elf_Word index{};
186,384✔
527
            unsigned type{};
186,384✔
528
            ELFIO::Elf_Sxword addend{};
186,384✔
529
            if (reloc.get_entry(i, offset, index, type, addend)) {
186,384✔
530
                if (offset < program_offset || offset >= program_offset + program_size) {
186,384✔
531
                    // Relocation is not for this program.
532
                    continue;
52✔
533
                }
534
                offset -= program_offset;
186,332✔
535
                const unsigned long location = offset / sizeof(ebpf_inst);
186,332✔
536
                try_reloc(section_name, inst_seq, location, index);
186,332✔
537
            }
538
        }
539
    }
540

541
    void read_programs() {
752✔
542
        for (const auto& section : reader.sections) {
24,200✔
543
            if (!(section->get_flags() & ELFIO::SHF_EXECINSTR)) {
23,448✔
544
                // Section does not contain executable instructions.
545
                continue;
16,378✔
546
            }
547
            const auto section_size = section->get_size();
7,738✔
548
            if (section_size == 0) {
7,738✔
549
                continue;
668✔
550
            }
551
            const auto section_data = section->get_data();
7,070✔
552
            if (section_data == nullptr) {
7,070✔
NEW
553
                continue;
×
554
            }
555
            const string section_name = section->get_name();
7,070✔
556
            const EbpfProgramType program_type = params.platform->get_program_type(section_name, params.path);
7,070✔
557

558
            for (ELFIO::Elf_Xword program_offset = 0; program_offset < section_size;) {
14,186✔
559
                auto [program_name, program_size] = get_program_name_and_size(*section, program_offset, symbols);
7,116✔
560
                std::vector<ebpf_inst> inst_seq = vector_of<ebpf_inst>(section_data + program_offset, program_size);
7,116✔
561
                update_relocations(inst_seq, section_name, program_offset, program_size);
7,116✔
562
                raw_programs.emplace_back(raw_program{
17,790✔
563
                    params.path,
7,116✔
564
                    section_name,
565
                    gsl::narrow_cast<uint32_t>(program_offset),
7,116✔
566
                    program_name,
567
                    std::move(inst_seq),
3,558✔
568
                    program_info{
569
                        .platform = params.platform,
7,116✔
570
                        .map_descriptors = global.map_descriptors,
7,116✔
571
                        .type = program_type,
572
                    },
573
                });
574
                program_offset += program_size;
7,116✔
575
            }
7,116✔
576
        }
7,070✔
577

578
        // Below, only relocations of symbols located in the map sections are allowed,
579
        // so if there are relocations there needs to be a maps section.
580
        if (!unresolved_symbols_errors.empty()) {
752✔
NEW
581
            for (const auto& unresolved_symbol : unresolved_symbols_errors) {
×
NEW
582
                std::cerr << unresolved_symbol << std::endl;
×
583
            }
NEW
584
            throw UnmarshalError("There are relocations in section but no maps sections in file " + params.path +
×
NEW
585
                                 "\nMake sure to inline all function calls.");
×
586
        }
587

588
        if (const auto btf_section = reader.sections[".BTF"]) {
752✔
589
            if (const auto btf_ext = reader.sections[".BTF.ext"]) {
186✔
590
                update_line_info(raw_programs, btf_section, btf_ext);
186✔
591
            }
592
        }
593

594
        // Now that we have all programs in the list, we can recursively append any subprograms
595
        // to the calling programs.  We have to keep them as programs themselves in case the caller
596
        // wants to verify them separately, but we also have to append them if used as subprograms to
597
        // allow the caller to be fully verified since inst.imm can only point into the same program.
598
        for (auto& prog : raw_programs) {
7,866✔
599
            std::string error = append_subprograms(prog);
7,116✔
600
            if (!error.empty()) {
7,116✔
601
                if (prog.section_name == params.desired_section) {
1,946✔
602
                    throw UnmarshalError(error);
2✔
603
                }
604
            }
605
        }
7,116✔
606

607
        // Now that we've incorporated any subprograms from other sections, we can narrow the list
608
        // to return to just those programs in the desired section, if any.
609
        if (!params.desired_section.empty() && !raw_programs.empty()) {
750✔
610
            for (int index = raw_programs.size() - 1; index >= 0; index--) {
7,784✔
611
                if (raw_programs[index].section_name != params.desired_section) {
7,072✔
612
                    raw_programs.erase(raw_programs.begin() + index);
6,318✔
613
                }
614
            }
615
        }
616

617
        if (raw_programs.empty()) {
750✔
618
            if (params.desired_section.empty()) {
2✔
NEW
619
                throw UnmarshalError("Can't find any non-empty TEXT sections in file " + params.path);
×
620
            }
621
            throw UnmarshalError("Can't find section " + params.desired_section + " in file " + params.path);
4✔
622
        }
623
    }
748✔
624
};
625

626
std::vector<raw_program> read_elf(std::istream& input_stream, const std::string& path,
754✔
627
                                  const std::string& desired_section, const ebpf_verifier_options_t& options,
628
                                  const ebpf_platform_t* platform) {
629
    const params_t params{.path = path, .options = options, .platform = platform, .desired_section = desired_section};
754✔
630
    const ELFIO::elfio reader = load_elf(input_stream, path);
754✔
631
    const ELFIO::const_symbol_section_accessor symbols = read_and_validate_symbol_section(reader, path);
754✔
632
    const global_data_t global = extract_global_data(params, reader, symbols);
752✔
633
    program_reader_t program_reader{params, reader, symbols, global};
752✔
634
    program_reader.read_programs();
752✔
635
    return std::move(program_reader.raw_programs);
1,122✔
636
}
765✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc