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

Alan-Jowett / ebpf-verifier / 18658728485

18 Oct 2025 05:56PM UTC coverage: 88.47% (+0.4%) from 88.11%
18658728485

push

github

elazarg
Bump external/bpf_conformance from `8f3c2fe` to `6fa6a20`

Bumps [external/bpf_conformance](https://github.com/Alan-Jowett/bpf_conformance) from `8f3c2fe` to `6fa6a20`.
- [Release notes](https://github.com/Alan-Jowett/bpf_conformance/releases)
- [Commits](https://github.com/Alan-Jowett/bpf_conformance/compare/8f3c2fe88...<a class=hub.com/Alan-Jowett/ebpf-verifier/commit/6fa6a20ac6fd3612ea9338312a67408687b9f06b">6fa6a20ac)

---
updated-dependencies:
- dependency-name: external/bpf_conformance
  dependency-version: 6fa6a20ac6fd3612ea9338312a67408687b9f06b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

8954 of 10121 relevant lines covered (88.47%)

18293099.16 hits per line

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

86.83
/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 <sstream>
7
#include <string>
8
#include <sys/stat.h>
9
#include <vector>
10

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

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

20
namespace prevail {
21

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

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

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

47
EbpfMapDescriptor* find_map_descriptor(const int map_fd) {
25,490✔
48
    for (EbpfMapDescriptor& map : thread_local_program_info->map_descriptors) {
234,282✔
49
        if (map.original_fd == map_fd) {
234,280✔
50
            return &map;
25,488✔
51
        }
52
    }
53
    return nullptr;
1✔
54
}
55

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

62
struct symbol_details_t {
336,343✔
63
    std::string name;
64
    ELFIO::Elf64_Addr value{}; // also relocation offset
65
    ELFIO::Elf_Xword size{};
66
    unsigned char bind{};
67
    unsigned char type{};
68
    ELFIO::Elf_Half section_index{};
69
    unsigned char other{};
70
};
71

72
static symbol_details_t get_symbol_details(const ELFIO::const_symbol_section_accessor& symbols,
30,441,468✔
73
                                           const ELFIO::Elf_Xword index) {
74
    symbol_details_t details;
30,441,468✔
75
    symbols.get_symbol(index, details.name, details.value, details.size, details.bind, details.type,
30,441,468✔
76
                       details.section_index, details.other);
30,441,468✔
77
    return details;
30,441,468✔
78
}
×
79

80
struct parse_params_t {
3✔
81
    const std::string& path;
82
    const ebpf_verifier_options_t& options;
83
    const ebpf_platform_t* platform;
84
    const std::string desired_section;
85
};
86

87
std::vector<RawProgram> read_elf(const std::string& path, const std::string& desired_section,
756✔
88
                                 const ebpf_verifier_options_t& options, const ebpf_platform_t* platform) {
89
    if (std::ifstream stream{path, std::ios::in | std::ios::binary}) {
756✔
90
        return read_elf(stream, path, desired_section, options, platform);
1,502✔
91
    }
756✔
92
    struct stat st;
1✔
93
    if (stat(path.c_str(), &st)) {
2✔
94
        throw UnmarshalError(std::string(strerror(errno)) + " opening " + path);
6✔
95
    }
96
    throw UnmarshalError("Can't process ELF file " + path);
×
97
}
98

99
static std::tuple<std::string, ELFIO::Elf_Xword>
100
get_program_name_and_size(const ELFIO::section& sec, const ELFIO::Elf_Xword start,
7,116✔
101
                          const ELFIO::const_symbol_section_accessor& symbols) {
102
    const ELFIO::Elf_Xword symbol_count = symbols.get_symbols_num();
7,116✔
103
    const ELFIO::Elf_Half section_index = sec.get_index();
7,116✔
104
    std::string program_name = sec.get_name();
7,116✔
105
    ELFIO::Elf_Xword size = sec.get_size() - start;
7,116✔
106
    for (ELFIO::Elf_Xword index = 0; index < symbol_count; index++) {
29,619,048✔
107
        auto symbol_details = get_symbol_details(symbols, index);
29,611,932✔
108
        if (symbol_details.section_index == section_index && !symbol_details.name.empty()) {
29,611,932✔
109
            if (symbol_details.type != ELFIO::STT_FUNC) {
258,322✔
110
                continue;
255,140✔
111
            }
112
            const auto relocation_offset = symbol_details.value;
3,182✔
113
            if (relocation_offset == start) {
3,182✔
114
                // We found the program name for this program.
115
                program_name = symbol_details.name;
14,681,366✔
116
            } else if (relocation_offset > start && relocation_offset < start + size) {
212✔
117
                // We found another program that follows, so truncate the size of this program.
118
                size = relocation_offset - start;
76✔
119
            }
120
        }
121
    }
29,611,932✔
122
    return {program_name, size};
14,232✔
123
}
7,116✔
124

125
static std::string bad_reloc_value(const size_t reloc_value) {
×
126
    return "Bad reloc value (" + std::to_string(reloc_value) + "). " + "Make sure to compile with -O2.";
×
127
}
128

129
// Structure used to keep track of subprogram relocation data until any subprograms
130
// are loaded and can be appended to the calling program.
131
struct FunctionRelocation {
68,676✔
132
    size_t prog_index{};              // Index of source program in std::vector of raw programs.
133
    ELFIO::Elf_Xword source_offset{}; // Instruction offset in source section of source instruction.
134
    ELFIO::Elf_Xword relocation_entry_index{};
135
    std::string target_function_name;
136
};
137

138
static RawProgram* find_subprogram(std::vector<RawProgram>& programs, const ELFIO::section& subprogram_section,
1,958✔
139
                                   const std::string& symbol_name) {
140
    // Find subprogram by name.
141
    for (auto& subprog : programs) {
186,646✔
142
        if (subprog.section_name == subprogram_section.get_name() && subprog.function_name == symbol_name) {
277,050✔
143
            return &subprog;
12✔
144
        }
145
    }
146
    return nullptr;
973✔
147
}
148

149
using MapOffsets = std::map<std::string, size_t>;
150

151
struct elf_global_data {
340✔
152
    std::set<ELFIO::Elf_Half> map_section_indices;
153
    std::vector<EbpfMapDescriptor> map_descriptors;
154
    std::variant<size_t, MapOffsets> map_record_size_or_map_offsets;
155
    std::set<ELFIO::Elf_Half> variable_section_indices;
156
};
157

158
static std::map<int, int> map_typeid_to_fd(const std::vector<EbpfMapDescriptor>& map_descriptors) {
116✔
159
    std::map<int, int> type_id_to_fd_map;
116✔
160
    int pseudo_fd = 1;
116✔
161
    // Gather the typeids for each map and assign a pseudo-fd to each map.
162
    for (const auto& map_descriptor : map_descriptors) {
208✔
163
        if (!type_id_to_fd_map.contains(map_descriptor.original_fd)) {
92✔
164
            type_id_to_fd_map[map_descriptor.original_fd] = pseudo_fd++;
92✔
165
        }
166
    }
167
    return type_id_to_fd_map;
116✔
168
}
×
169

170
static ELFIO::const_symbol_section_accessor read_and_validate_symbol_section(const ELFIO::elfio& reader,
754✔
171
                                                                             const std::string& path) {
172
    const ELFIO::section* symbol_section = reader.sections[".symtab"];
754✔
173
    if (!symbol_section) {
754✔
174
        throw UnmarshalError("No symbol section found in ELF file " + path);
×
175
    }
176

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

181
    if (symbol_section->get_entry_size() != expected_entry_size) {
754✔
182
        throw UnmarshalError("Invalid symbol section found in ELF file " + path);
3✔
183
    }
184
    return ELFIO::const_symbol_section_accessor{reader, symbol_section};
752✔
185
}
186

187
static ELFIO::elfio load_elf(std::istream& input_stream, const std::string& path) {
754✔
188
    ELFIO::elfio reader;
754✔
189
    if (!reader.load(input_stream)) {
754✔
190
        throw UnmarshalError("Can't process ELF file " + path);
×
191
    }
192
    return reader;
754✔
193
}
×
194

195
static void dump_btf_types(const libbtf::btf_type_data& btf_data, const std::string& path) {
×
196
    std::stringstream output;
×
197
    std::cout << "Dumping BTF data for" << path << std::endl;
×
198
    // Dump the BTF data to stdout for debugging purposes.
199
    btf_data.to_json(output);
×
200
    std::cout << libbtf::pretty_print_json(output.str()) << std::endl;
×
201
    std::cout << std::endl;
×
202
}
×
203

204
static void update_line_info(std::vector<RawProgram>& raw_programs, const ELFIO::section* btf_section,
186✔
205
                             const ELFIO::section* btf_ext) {
206
    auto visitor = [&raw_programs](const std::string& section, const uint32_t instruction_offset,
1,256,823✔
207
                                   const std::string& file_name, const std::string& source, const uint32_t line_number,
208
                                   const uint32_t column_number) {
209
        for (auto& program : raw_programs) {
61,377,566✔
210
            if (program.section_name == section && instruction_offset >= program.insn_off &&
60,749,352✔
211
                instruction_offset < program.insn_off + program.prog.size() * sizeof(EbpfInst)) {
1,257,032✔
212
                const size_t inst_index = (instruction_offset - program.insn_off) / sizeof(EbpfInst);
1,256,730✔
213
                if (inst_index >= program.prog.size()) {
1,256,730✔
214
                    throw UnmarshalError("Invalid BTF data");
×
215
                }
216
                program.info.line_info.insert_or_assign(inst_index,
628,365✔
217
                                                        btf_line_info_t{file_name, source, line_number, column_number});
1,885,095✔
218
            }
219
        }
220
    };
1,256,730✔
221

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

224
    // BTF doesn't include line info for every instruction, only on the first instruction per source line.
225
    for (auto& program : raw_programs) {
3,154✔
226
        for (size_t i = 1; i < program.prog.size(); i++) {
4,884,832✔
227
            // If the previous PC has line info, copy it.
228
            if (program.info.line_info[i].line_number == 0 && program.info.line_info[i - 1].line_number != 0) {
4,881,864✔
229
                program.info.line_info[i] = program.info.line_info[i - 1];
3,782,354✔
230
            }
231
        }
232
    }
233
}
186✔
234

235
static elf_global_data parse_btf_section(const parse_params_t& parse_params, const ELFIO::elfio& reader) {
188✔
236
    const auto btf_section = reader.sections[".BTF"];
188✔
237
    if (!btf_section) {
188✔
238
        return {};
72✔
239
    }
240
    const libbtf::btf_type_data btf_data(vector_of<std::byte>(*btf_section), false);
116✔
241
    if (parse_params.options.verbosity_opts.dump_btf_types_json) {
116✔
242
        dump_btf_types(btf_data, parse_params.path);
×
243
    }
244

245
    elf_global_data global;
116✔
246

247
    {
58✔
248
        MapOffsets map_offsets;
116✔
249
        for (const auto& map : parse_btf_map_section(btf_data)) {
208✔
250
            map_offsets.emplace(map.name, global.map_descriptors.size());
92✔
251
            global.map_descriptors.push_back({
92✔
252
                .original_fd = gsl::narrow_cast<int>(map.type_id),
92✔
253
                .type = map.map_type,
92✔
254
                .key_size = map.key_size,
92✔
255
                .value_size = map.value_size,
92✔
256
                .max_entries = map.max_entries,
92✔
257
                .inner_map_fd = map.inner_map_type_id != 0 ? map.inner_map_type_id : DEFAULT_MAP_FD,
92✔
258
            });
259
        }
116✔
260
        global.map_record_size_or_map_offsets = std::move(map_offsets);
116✔
261
    }
58✔
262

263
    {
58✔
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(global.map_descriptors);
116✔
268
        for (auto& map_descriptor : global.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
    }
58✔
275

276
    if (const auto maps_section = reader.sections[".maps"]) {
116✔
277
        global.map_section_indices.insert(maps_section->get_index());
74✔
278
    }
279

280
    for (const auto section_name : {".rodata", ".data", ".bss"}) {
464✔
281
        if (const auto section = reader.sections[section_name]) {
348✔
282
            if (section->get_size() != 0) {
6✔
283
                global.variable_section_indices.insert(section->get_index());
6✔
284
            }
285
        }
286
    }
287
    return global;
116✔
288
}
188✔
289

290
// parse_maps_sections processes all maps sections in the provided ELF file by calling the platform-specific maps'
291
// parser. The section index of each maps section is inserted into map_section_indices.
292
static elf_global_data parse_map_sections(const parse_params_t& parse_params, const ELFIO::elfio& reader,
564✔
293
                                          const ELFIO::const_symbol_section_accessor& symbols) {
294
    elf_global_data global;
564✔
295
    size_t map_record_size = parse_params.platform->map_record_size;
564✔
296
    for (ELFIO::Elf_Half i = 0; i < reader.sections.size(); ++i) {
20,532✔
297
        const auto s = reader.sections[i];
19,968✔
298
        if (!is_map_section(s->get_name())) {
19,968✔
299
            continue;
19,402✔
300
        }
301

302
        // Count the number of symbols that point into this maps section.
303
        int map_count = 0;
283✔
304
        for (ELFIO::Elf_Xword index = 0; index < symbols.get_symbols_num(); index++) {
484,974✔
305
            const auto symbol_details = get_symbol_details(symbols, index);
484,408✔
306
            if (symbol_details.section_index == i && !symbol_details.name.empty()) {
484,408✔
307
                map_count++;
5,800✔
308
            }
309
        }
484,408✔
310

311
        if (map_count > 0) {
566✔
312
            map_record_size = s->get_size() / map_count;
566✔
313
            if (s->get_data() == nullptr || map_record_size == 0) {
566✔
314
                throw UnmarshalError("bad maps section");
×
315
            }
316
            if (s->get_size() % map_record_size != 0) {
566✔
317
                throw UnmarshalError("bad maps section size");
×
318
            }
319
            parse_params.platform->parse_maps_section(global.map_descriptors, s->get_data(), map_record_size, map_count,
566✔
320
                                                      parse_params.platform, parse_params.options);
566✔
321
        }
322
        global.map_section_indices.insert(s->get_index());
566✔
323
    }
324
    parse_params.platform->resolve_inner_map_references(global.map_descriptors);
564✔
325
    global.map_record_size_or_map_offsets = map_record_size;
564✔
326
    return global;
846✔
327
}
×
328

329
static elf_global_data extract_global_data(const parse_params_t& parse_params, const ELFIO::elfio& reader,
752✔
330
                                           const ELFIO::const_symbol_section_accessor& symbols) {
331
    if (std::ranges::any_of(reader.sections, [](const auto& section) { return is_map_section(section->get_name()); })) {
19,738✔
332
        return parse_map_sections(parse_params, reader, symbols);
564✔
333
    }
334
    return parse_btf_section(parse_params, reader);
188✔
335
}
336

337
class ProgramReader {
338
    const parse_params_t& parse_params;
339
    const ELFIO::elfio& reader;
340
    const ELFIO::const_symbol_section_accessor& symbols;
341
    const elf_global_data& global;
342

343
    std::vector<FunctionRelocation> function_relocations;
344
    std::vector<std::string> unresolved_symbol_errors;
345
    std::map<const RawProgram*, bool> resolved_subprograms;
346

347
  public:
348
    std::vector<RawProgram> raw_programs;
349

350
    ProgramReader(const parse_params_t& parse_params, const ELFIO::elfio& reader,
752✔
351
                  const ELFIO::const_symbol_section_accessor& symbols, const elf_global_data& global)
352
        : parse_params{parse_params}, reader{reader}, symbols{symbols}, global{global} {}
752✔
353

354
    // Returns an error message, or empty string on success.
355
    std::string append_subprograms(RawProgram& prog) {
7,128✔
356
        if (resolved_subprograms[&prog]) {
7,128✔
357
            // We've already appended any relevant subprograms.
358
            return {};
12✔
359
        }
360
        resolved_subprograms[&prog] = true;
7,116✔
361

362
        // Perform function relocations and fill in the inst.imm values of CallLocal instructions.
363
        std::map<std::string, ELFIO::Elf_Xword> subprogram_offsets;
7,116✔
364
        for (const auto& reloc : function_relocations) {
1,880,892✔
365
            if (reloc.prog_index >= raw_programs.size()) {
1,875,722✔
366
                continue;
1,873,764✔
367
            }
368
            if (raw_programs[reloc.prog_index].function_name != prog.function_name) {
1,875,722✔
369
                continue;
1,873,752✔
370
            }
371

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

376
                const auto symbol_details = get_symbol_details(symbols, reloc.relocation_entry_index);
1,958✔
377
                if (symbol_details.section_index >= reader.sections.size()) {
1,958✔
378
                    throw UnmarshalError("Invalid section index " + std::to_string(symbol_details.section_index) +
×
379
                                         " at source offset " + std::to_string(reloc.source_offset));
×
380
                }
381
                const ELFIO::section& subprogram_section = *reader.sections[symbol_details.section_index];
1,958✔
382

383
                if (const auto subprogram = find_subprogram(raw_programs, subprogram_section, symbol_details.name)) {
1,958✔
384
                    // Don't permit recursive subprograms.
385
                    if (subprogram == &prog) {
12✔
386
                        throw UnmarshalError("Subprogram '" + symbol_details.name +
×
387
                                             "' is the same as the calling program");
×
388
                    }
389

390
                    // Make sure subprogram has already had any subprograms of its own appended.
391
                    std::string error = append_subprograms(*subprogram);
12✔
392
                    if (!error.empty()) {
12✔
393
                        return error;
×
394
                    }
395

396
                    // Append subprogram to program.
397
                    prog.prog.insert(prog.prog.end(), subprogram->prog.begin(), subprogram->prog.end());
12✔
398
                    for (size_t i = 0; i < subprogram->info.line_info.size(); i++) {
216✔
399
                        prog.info.line_info[prog.info.line_info.size()] = subprogram->info.line_info[i];
204✔
400
                    }
401
                } else {
12✔
402
                    // The program will be invalid, but continue rather than throwing an exception
403
                    // since we might be verifying a different program in the file.
404
                    return std::string("Subprogram '" + symbol_details.name + "' not found in section '" +
4,865✔
405
                                       subprogram_section.get_name() + "'");
5,838✔
406
                }
407
            }
1,958✔
408

409
            // Fill in the PC offset into the imm field of the CallLocal instruction.
410
            const int64_t target_offset = gsl::narrow_cast<int64_t>(subprogram_offsets[reloc.target_function_name]);
24✔
411
            const auto offset_diff = target_offset - gsl::narrow<int64_t>(reloc.source_offset) - 1;
24✔
412
            if (offset_diff < std::numeric_limits<int32_t>::min() ||
36✔
413
                offset_diff > std::numeric_limits<int32_t>::max()) {
12✔
414
                throw UnmarshalError("Offset difference out of int32_t range for instruction at source offset " +
×
415
                                     std::to_string(reloc.source_offset));
×
416
            }
417
            prog.prog[reloc.source_offset].imm = gsl::narrow_cast<int32_t>(offset_diff);
24✔
418
        }
419
        return {};
5,170✔
420
    }
7,116✔
421

422
    int relocate_map(const std::string& symbol_name, const ELFIO::Elf_Word index) const {
156,922✔
423
        // Relocation value is an offset into the "maps" or ".maps" section.
424
        size_t reloc_value{};
156,922✔
425
        if (const auto* map_record_size = std::get_if<size_t>(&global.map_record_size_or_map_offsets)) {
156,922✔
426
            // The older maps section format uses a single map_record_size value,
427
            // so we can calculate the map descriptor index directly.
428
            const auto symbol_details = get_symbol_details(symbols, index);
156,838✔
429
            const auto relocation_offset = symbol_details.value;
156,838✔
430
            reloc_value = relocation_offset / *map_record_size;
156,838✔
431
        } else {
156,838✔
432
            // The newer .maps section format uses a variable-length map descriptor array,
433
            // so we need to look up the map descriptor index in a map.
434
            const auto& map_descriptors_offsets = std::get<MapOffsets>(global.map_record_size_or_map_offsets);
84✔
435
            const auto it = map_descriptors_offsets.find(symbol_name);
84✔
436
            if (it == map_descriptors_offsets.end()) {
84✔
437
                throw UnmarshalError("Map descriptor not found for symbol " + symbol_name);
×
438
            }
439
            reloc_value = it->second;
84✔
440
        }
441
        if (reloc_value >= global.map_descriptors.size()) {
156,922✔
442
            throw UnmarshalError(bad_reloc_value(reloc_value));
×
443
        }
444
        return global.map_descriptors.at(reloc_value).original_fd;
156,922✔
445
    }
446

447
    int relocate_global_variable(const std::string& symbol_name) const {
8✔
448
        const auto map_descriptors_offsets = std::get_if<MapOffsets>(&global.map_record_size_or_map_offsets);
8✔
449
        if (!map_descriptors_offsets) {
8✔
450
            throw UnmarshalError("Invalid map_offsets");
×
451
        }
452
        const auto it = map_descriptors_offsets->find(symbol_name);
8✔
453
        if (it == map_descriptors_offsets->end()) {
8✔
454
            throw UnmarshalError("Map descriptor not found for symbol " + symbol_name);
×
455
        }
456
        const size_t reloc_value = it->second;
8✔
457
        if (reloc_value >= global.map_descriptors.size()) {
8✔
458
            throw UnmarshalError(bad_reloc_value(reloc_value));
×
459
        }
460
        return global.map_descriptors.at(reloc_value).original_fd;
12✔
461
    }
462

463
    bool try_reloc(const std::string& symbol_name, const ELFIO::Elf_Half symbol_section_index,
186,332✔
464
                   std::vector<EbpfInst>& instructions, const size_t location, const ELFIO::Elf_Word index) {
465
        EbpfInst& instruction_to_relocate = instructions[location];
186,332✔
466

467
        // Queue up relocation for function symbols.
468
        if (instruction_to_relocate.opcode == INST_OP_CALL && instruction_to_relocate.src == INST_CALL_LOCAL) {
186,332✔
469
            function_relocations.emplace_back(FunctionRelocation{
29,402✔
470
                .prog_index = raw_programs.size(),
29,402✔
471
                .source_offset = location,
472
                .relocation_entry_index = index,
29,402✔
473
                .target_function_name = symbol_name,
474
            });
475
            return true;
29,402✔
476
        }
477

478
        // Verify that this is a map or global variable relocation.
479
        if ((instruction_to_relocate.opcode & INST_CLS_MASK) != INST_CLS_LD) {
156,930✔
480
            throw UnmarshalError("Illegal operation on symbol " + symbol_name + " at location " +
×
481
                                 std::to_string(location));
×
482
        }
483

484
        // Perform relocation for symbols located in the maps section.
485
        if (global.map_section_indices.contains(symbol_section_index)) {
156,930✔
486
            instruction_to_relocate.src = INST_LD_MODE_MAP_FD;
156,922✔
487
            instruction_to_relocate.imm = relocate_map(symbol_name, index);
156,922✔
488
            return true;
156,922✔
489
        }
490

491
        if (global.variable_section_indices.contains(symbol_section_index)) {
8✔
492
            // Load instructions are two instructions long, so we need to check the next instruction.
493
            if (instructions.size() <= location + 1) {
8✔
494
                throw UnmarshalError("Invalid relocation data");
×
495
            }
496
            // Copy the immediate value to the next instruction.
497
            instructions[location + 1].imm = instruction_to_relocate.imm;
8✔
498
            instruction_to_relocate.src = INST_LD_MODE_MAP_VALUE;
8✔
499
            instruction_to_relocate.imm = relocate_global_variable(reader.sections[symbol_section_index]->get_name());
12✔
500
            return true;
8✔
501
        }
502
        return false;
503
    }
504

505
    void process_relocations(std::vector<EbpfInst>& instructions, const ELFIO::const_relocation_section_accessor& reloc,
6,676✔
506
                             const std::string& section_name, const ELFIO::Elf_Xword program_offset,
507
                             const size_t program_size) {
508
        for (ELFIO::Elf_Xword i = 0; i < reloc.get_entries_num(); i++) {
193,060✔
509
            ELFIO::Elf64_Addr offset{};
186,384✔
510
            ELFIO::Elf_Word index{};
186,384✔
511
            unsigned type{};
186,384✔
512
            ELFIO::Elf_Sxword addend{};
186,384✔
513
            if (reloc.get_entry(i, offset, index, type, addend)) {
186,384✔
514
                if (offset < program_offset || offset >= program_offset + program_size) {
186,384✔
515
                    // Relocation is not for this program.
516
                    continue;
52✔
517
                }
518
                offset -= program_offset;
186,332✔
519
                const unsigned long location = offset / sizeof(EbpfInst);
186,332✔
520
                if (location >= instructions.size()) {
186,332✔
521
                    throw UnmarshalError("Invalid relocation data");
×
522
                }
523

524
                const auto symbol_details = get_symbol_details(symbols, index);
186,332✔
525
                if (!try_reloc(symbol_details.name, symbol_details.section_index, instructions, location, index)) {
186,332✔
526
                    unresolved_symbol_errors.push_back("Unresolved external symbol " + symbol_details.name +
×
527
                                                       " in section " + section_name + " at location " +
×
528
                                                       std::to_string(location));
×
529
                }
530
            }
186,332✔
531
        }
532
    }
6,676✔
533

534
    const ELFIO::section* get_relocation_section(const std::string& section_name) const {
7,116✔
535
        const ELFIO::section* prelocs = reader.sections[".rel" + section_name];
7,116✔
536
        if (!prelocs) {
7,116✔
537
            prelocs = reader.sections[".rela" + section_name];
1,070✔
538
        }
539
        if (!prelocs) {
4,093✔
540
            return nullptr;
220✔
541
        }
542
        if (!prelocs->get_data()) {
6,676✔
543
            throw UnmarshalError("Malformed relocation data");
×
544
        }
545
        return prelocs;
3,338✔
546
    }
547

548
    void read_programs() {
752✔
549
        for (const auto& section : reader.sections) {
24,200✔
550
            if (!(section->get_flags() & ELFIO::SHF_EXECINSTR)) {
23,448✔
551
                // Section does not contain executable instructions.
552
                continue;
16,378✔
553
            }
554
            const auto section_size = section->get_size();
7,738✔
555
            if (section_size == 0) {
7,738✔
556
                continue;
668✔
557
            }
558
            const auto section_data = section->get_data();
7,070✔
559
            if (section_data == nullptr) {
7,070✔
560
                continue;
×
561
            }
562
            const std::string section_name = section->get_name();
7,070✔
563
            const EbpfProgramType program_type =
3,535✔
564
                parse_params.platform->get_program_type(section_name, parse_params.path);
7,070✔
565

566
            for (ELFIO::Elf_Xword program_offset = 0; program_offset < section_size;) {
14,186✔
567
                auto [program_name, program_size] = get_program_name_and_size(*section, program_offset, symbols);
7,116✔
568
                std::vector<EbpfInst> instructions = vector_of<EbpfInst>(section_data + program_offset, program_size);
7,116✔
569

570
                if (const ELFIO::section* reloc_section = get_relocation_section(section_name)) {
7,116✔
571
                    const ELFIO::const_relocation_section_accessor reloc{reader, reloc_section};
6,676✔
572
                    process_relocations(instructions, reloc, section_name, program_offset, program_size);
6,676✔
573
                }
574

575
                raw_programs.emplace_back(RawProgram{
17,790✔
576
                    parse_params.path,
7,116✔
577
                    section_name,
578
                    gsl::narrow_cast<uint32_t>(program_offset),
7,116✔
579
                    program_name,
580
                    std::move(instructions),
3,558✔
581
                    ProgramInfo{
582
                        .platform = parse_params.platform,
7,116✔
583
                        .map_descriptors = global.map_descriptors,
7,116✔
584
                        .type = program_type,
585
                    },
586
                });
587
                program_offset += program_size;
7,116✔
588
            }
7,116✔
589
        }
7,070✔
590

591
        // Below, only relocations of symbols located in the maps sections are allowed,
592
        // so if there are relocations there needs to be a maps section.
593
        if (!unresolved_symbol_errors.empty()) {
752✔
594
            for (const auto& unresolved_symbol : unresolved_symbol_errors) {
×
595
                std::cerr << unresolved_symbol << std::endl;
×
596
            }
597
            throw UnmarshalError("There are relocations in section but no maps sections in file " + parse_params.path +
×
598
                                 "\nMake sure to inline all function calls.");
×
599
        }
600

601
        if (const auto btf_section = reader.sections[".BTF"]) {
752✔
602
            if (const auto btf_ext = reader.sections[".BTF.ext"]) {
186✔
603
                update_line_info(raw_programs, btf_section, btf_ext);
186✔
604
            }
605
        }
606

607
        // Now that we have all programs in the list, we can recursively append any subprograms
608
        // to the calling programs.  We have to keep them as programs themselves in case the caller
609
        // wants to verify them separately, but we also have to append them if used as subprograms to
610
        // allow the caller to be fully verified since inst.imm can only point into the same program.
611
        for (auto& prog : raw_programs) {
7,866✔
612
            std::string error = append_subprograms(prog);
7,116✔
613
            if (!error.empty()) {
7,116✔
614
                if (prog.section_name == parse_params.desired_section) {
1,946✔
615
                    throw UnmarshalError(error);
2✔
616
                }
617
            }
618
        }
7,116✔
619

620
        // Now that we've incorporated any subprograms from other sections, we can narrow the list
621
        // to return to just those programs in the desired section, if any.
622
        if (!parse_params.desired_section.empty() && !raw_programs.empty()) {
750✔
623
            for (int index = raw_programs.size() - 1; index >= 0; index--) {
7,784✔
624
                if (raw_programs[index].section_name != parse_params.desired_section) {
7,072✔
625
                    raw_programs.erase(raw_programs.begin() + index);
6,318✔
626
                }
627
            }
628
        }
629

630
        if (raw_programs.empty()) {
750✔
631
            if (parse_params.desired_section.empty()) {
2✔
632
                throw UnmarshalError("Can't find any non-empty TEXT sections in file " + parse_params.path);
×
633
            }
634
            throw UnmarshalError("Can't find section " + parse_params.desired_section + " in file " +
4✔
635
                                 parse_params.path);
6✔
636
        }
637
    }
748✔
638
};
639

640
std::vector<RawProgram> read_elf(std::istream& input_stream, const std::string& path,
754✔
641
                                 const std::string& desired_section, const ebpf_verifier_options_t& options,
642
                                 const ebpf_platform_t* platform) {
643
    const parse_params_t parse_params{
377✔
644
        .path = path, .options = options, .platform = platform, .desired_section = desired_section};
754✔
645
    const ELFIO::elfio reader = load_elf(input_stream, path);
754✔
646
    const ELFIO::const_symbol_section_accessor symbols = read_and_validate_symbol_section(reader, path);
754✔
647
    const elf_global_data global = extract_global_data(parse_params, reader, symbols);
752✔
648
    ProgramReader program_reader{parse_params, reader, symbols, global};
752✔
649
    program_reader.read_programs();
752✔
650
    return std::move(program_reader.raw_programs);
1,122✔
651
}
765✔
652
} // namespace prevail
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