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

vbpf / ebpf-verifier / 14231336081

02 Apr 2025 11:08PM UTC coverage: 87.272% (-0.9%) from 88.177%
14231336081

push

github

web-flow
Propogate ebpf_verifier_options_t to thread_local_options (#856)

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

5 of 5 new or added lines in 2 files covered. (100.0%)

58 existing lines in 19 files now uncovered.

8324 of 9538 relevant lines covered (87.27%)

4881701.3 hits per line

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

86.0
/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
template <typename T>
20
    requires std::is_trivially_copyable_v<T>
21
static std::vector<T> vector_of(const char* data, const ELFIO::Elf_Xword size) {
3,802✔
22
    if (size % sizeof(T) != 0 || size > std::numeric_limits<uint32_t>::max() || !data) {
3,802✔
23
        throw UnmarshalError("Invalid argument to vector_of");
×
24
    }
25
    return {reinterpret_cast<const T*>(data), reinterpret_cast<const T*>(data + size)};
3,802✔
26
}
27

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

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

44
EbpfMapDescriptor* find_map_descriptor(const int map_fd) {
12,745✔
45
    for (EbpfMapDescriptor& map : thread_local_program_info->map_descriptors) {
117,145✔
46
        if (map.original_fd == map_fd) {
117,144✔
47
            return &map;
12,744✔
48
        }
49
    }
50
    return nullptr;
51
}
52

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

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

69
static symbol_details_t get_symbol_details(const ELFIO::const_symbol_section_accessor& symbols,
15,220,734✔
70
                                           const ELFIO::Elf_Xword index) {
71
    symbol_details_t details;
15,220,734✔
72
    symbols.get_symbol(index, details.name, details.value, details.size, details.bind, details.type,
15,220,734✔
73
                       details.section_index, details.other);
15,220,734✔
74
    return details;
15,220,734✔
75
}
×
76

77
struct parse_params_t {
3✔
78
    const std::string& path;
79
    const ebpf_verifier_options_t& options;
80
    const ebpf_platform_t* platform;
81
    const std::string desired_section;
82
};
83

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

96
static std::tuple<std::string, ELFIO::Elf_Xword>
97
get_program_name_and_size(const ELFIO::section& sec, const ELFIO::Elf_Xword start,
3,558✔
98
                          const ELFIO::const_symbol_section_accessor& symbols) {
99
    const ELFIO::Elf_Xword symbol_count = symbols.get_symbols_num();
3,558✔
100
    const ELFIO::Elf_Half section_index = sec.get_index();
3,558✔
101
    std::string program_name = sec.get_name();
3,558✔
102
    ELFIO::Elf_Xword size = sec.get_size() - start;
3,558✔
103
    for (ELFIO::Elf_Xword index = 0; index < symbol_count; index++) {
14,809,524✔
104
        auto symbol_details = get_symbol_details(symbols, index);
14,805,966✔
105
        if (symbol_details.section_index == section_index && !symbol_details.name.empty()) {
14,805,966✔
106
            if (symbol_details.type != ELFIO::STT_FUNC) {
129,161✔
107
                continue;
127,570✔
108
            }
109
            const auto relocation_offset = symbol_details.value;
1,591✔
110
            if (relocation_offset == start) {
1,591✔
111
                // We found the program name for this program.
112
                program_name = symbol_details.name;
14,679,881✔
113
            } else if (relocation_offset > start && relocation_offset < start + size) {
106✔
114
                // We found another program that follows, so truncate the size of this program.
115
                size = relocation_offset - start;
38✔
116
            }
117
        }
118
    }
14,805,966✔
119
    return {program_name, size};
7,116✔
120
}
3,558✔
121

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

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

135
static raw_program* find_subprogram(std::vector<raw_program>& programs, const ELFIO::section& subprogram_section,
979✔
136
                                    const std::string& symbol_name) {
137
    // Find subprogram by name.
138
    for (auto& subprog : programs) {
93,323✔
139
        if (subprog.section_name == subprogram_section.get_name() && subprog.function_name == symbol_name) {
184,700✔
140
            return &subprog;
6✔
141
        }
142
    }
143
    return nullptr;
144
}
145

146
using map_offsets_t = std::map<std::string, size_t>;
147

148
struct elf_global_data {
340✔
149
    std::set<ELFIO::Elf_Half> map_section_indices;
150
    std::vector<EbpfMapDescriptor> map_descriptors;
151
    std::variant<size_t, map_offsets_t> map_record_size_or_map_offsets;
152
    std::set<ELFIO::Elf_Half> variable_section_indices;
153
};
154

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

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

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

178
    if (symbol_section->get_entry_size() != expected_entry_size) {
377✔
179
        throw UnmarshalError("Invalid symbol section found in ELF file " + path);
2✔
180
    }
181
    return ELFIO::const_symbol_section_accessor{reader, symbol_section};
376✔
182
}
183

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

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

201
static void update_line_info(std::vector<raw_program>& raw_programs, const ELFIO::section* btf_section,
93✔
202
                             const ELFIO::section* btf_ext) {
203
    auto visitor = [&raw_programs](const std::string& section, const uint32_t instruction_offset,
628,458✔
204
                                   const std::string& file_name, const std::string& source, const uint32_t line_number,
205
                                   const uint32_t column_number) {
206
        for (auto& program : raw_programs) {
30,688,783✔
207
            if (program.section_name == section && instruction_offset >= program.insn_off &&
30,060,418✔
208
                instruction_offset < program.insn_off + program.prog.size() * sizeof(ebpf_inst)) {
628,516✔
209
                const size_t inst_index = (instruction_offset - program.insn_off) / sizeof(ebpf_inst);
628,365✔
210
                if (inst_index >= program.prog.size()) {
628,365✔
211
                    throw UnmarshalError("Invalid BTF data");
×
212
                }
213
                program.info.line_info.insert_or_assign(inst_index,
628,365✔
214
                                                        btf_line_info_t{file_name, source, line_number, column_number});
1,256,730✔
215
            }
216
        }
217
    };
628,365✔
218

219
    libbtf::btf_parse_line_information(vector_of<std::byte>(*btf_section), vector_of<std::byte>(*btf_ext), visitor);
186✔
220

221
    // BTF doesn't include line info for every instruction, only on the first instruction per source line.
222
    for (auto& program : raw_programs) {
1,577✔
223
        for (size_t i = 1; i < program.prog.size(); i++) {
2,442,416✔
224
            // If the previous PC has line info, copy it.
225
            if (program.info.line_info[i].line_number == 0 && program.info.line_info[i - 1].line_number != 0) {
2,440,932✔
226
                program.info.line_info[i] = program.info.line_info[i - 1];
1,891,177✔
227
            }
228
        }
229
    }
230
}
93✔
231

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

242
    elf_global_data global;
58✔
243

244
    {
58✔
245
        map_offsets_t map_offsets;
58✔
246
        for (const auto& map : parse_btf_map_section(btf_data)) {
104✔
247
            map_offsets.emplace(map.name, global.map_descriptors.size());
46✔
248
            global.map_descriptors.push_back({
46✔
249
                .original_fd = gsl::narrow_cast<int>(map.type_id),
46✔
250
                .type = map.map_type,
46✔
251
                .key_size = map.key_size,
46✔
252
                .value_size = map.value_size,
46✔
253
                .max_entries = map.max_entries,
46✔
254
                .inner_map_fd = map.inner_map_type_id != 0 ? map.inner_map_type_id : DEFAULT_MAP_FD,
46✔
255
            });
256
        }
58✔
257
        global.map_record_size_or_map_offsets = std::move(map_offsets);
58✔
UNCOV
258
    }
×
259

260
    {
58✔
261
        // Prevail requires:
262
        // Map fds are sequential starting from 1.
263
        // Map fds are assigned in the order of the maps in the ELF file.
264
        const std::map<int, int> type_id_to_fd_map = map_typeid_to_fd(global.map_descriptors);
58✔
265
        for (auto& map_descriptor : global.map_descriptors) {
104✔
266
            map_descriptor.original_fd = type_id_to_fd_map.at(map_descriptor.original_fd);
46✔
267
            if (map_descriptor.inner_map_fd != DEFAULT_MAP_FD) {
46✔
268
                map_descriptor.inner_map_fd = type_id_to_fd_map.at(map_descriptor.inner_map_fd);
3✔
269
            }
270
        }
UNCOV
271
    }
×
272

273
    if (const auto maps_section = reader.sections[".maps"]) {
58✔
274
        global.map_section_indices.insert(maps_section->get_index());
37✔
275
    }
276

277
    for (const auto section_name : {".rodata", ".data", ".bss"}) {
232✔
278
        if (const auto section = reader.sections[section_name]) {
174✔
279
            if (section->get_size() != 0) {
3✔
280
                global.variable_section_indices.insert(section->get_index());
3✔
281
            }
282
        }
283
    }
284
    return global;
58✔
285
}
94✔
286

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

299
        // Count the number of symbols that point into this maps section.
300
        int map_count = 0;
301
        for (ELFIO::Elf_Xword index = 0; index < symbols.get_symbols_num(); index++) {
242,487✔
302
            const auto symbol_details = get_symbol_details(symbols, index);
242,204✔
303
            if (symbol_details.section_index == i && !symbol_details.name.empty()) {
242,204✔
304
                map_count++;
2,900✔
305
            }
306
        }
242,204✔
307

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

326
static elf_global_data extract_global_data(const parse_params_t& parse_params, const ELFIO::elfio& reader,
376✔
327
                                           const ELFIO::const_symbol_section_accessor& symbols) {
328
    if (std::ranges::any_of(reader.sections, [](const auto& section) { return is_map_section(section->get_name()); })) {
9,869✔
329
        return parse_map_sections(parse_params, reader, symbols);
282✔
330
    }
331
    return parse_btf_section(parse_params, reader);
94✔
332
}
333

334
class program_reader_t {
335
    const parse_params_t& parse_params;
336
    const ELFIO::elfio& reader;
337
    const ELFIO::const_symbol_section_accessor& symbols;
338
    const elf_global_data& global;
339

340
    std::vector<function_relocation> function_relocations;
341
    std::vector<std::string> unresolved_symbol_errors;
342
    std::map<const raw_program*, bool> resolved_subprograms;
343

344
  public:
345
    std::vector<raw_program> raw_programs;
346

347
    program_reader_t(const parse_params_t& parse_params, const ELFIO::elfio& reader,
376✔
348
                     const ELFIO::const_symbol_section_accessor& symbols, const elf_global_data& global)
349
        : parse_params{parse_params}, reader{reader}, symbols{symbols}, global{global} {}
376✔
350

351
    // Returns an error message, or empty string on success.
352
    std::string append_subprograms(raw_program& prog) {
3,564✔
353
        if (resolved_subprograms[&prog]) {
3,564✔
354
            // We've already appended any relevant subprograms.
355
            return {};
6✔
356
        }
357
        resolved_subprograms[&prog] = true;
3,558✔
358

359
        // Perform function relocations and fill in the inst.imm values of CallLocal instructions.
360
        std::map<std::string, ELFIO::Elf_Xword> subprogram_offsets;
3,558✔
361
        for (const auto& reloc : function_relocations) {
940,446✔
362
            if (reloc.prog_index >= raw_programs.size()) {
937,861✔
363
                continue;
936,888✔
364
            }
365
            if (raw_programs[reloc.prog_index].function_name != prog.function_name) {
937,861✔
366
                continue;
936,876✔
367
            }
368

369
            // Check whether we already appended the target program, and append it if not.
370
            if (!subprogram_offsets.contains(reloc.target_function_name)) {
985✔
371
                subprogram_offsets[reloc.target_function_name] = prog.prog.size();
979✔
372

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

380
                if (const auto subprogram = find_subprogram(raw_programs, subprogram_section, symbol_details.name)) {
979✔
381
                    // Don't permit recursive subprograms.
382
                    if (subprogram == &prog) {
6✔
383
                        throw UnmarshalError("Subprogram '" + symbol_details.name +
×
384
                                             "' is the same as the calling program");
×
385
                    }
386

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

393
                    // Append subprogram to program.
394
                    prog.prog.insert(prog.prog.end(), subprogram->prog.begin(), subprogram->prog.end());
6✔
395
                    for (size_t i = 0; i < subprogram->info.line_info.size(); i++) {
108✔
396
                        prog.info.line_info[prog.info.line_info.size()] = subprogram->info.line_info[i];
102✔
397
                    }
398
                } else {
6✔
399
                    // The program will be invalid, but continue rather than throwing an exception
400
                    // since we might be verifying a different program in the file.
401
                    return std::string("Subprogram '" + symbol_details.name + "' not found in section '" +
2,919✔
402
                                       subprogram_section.get_name() + "'");
2,919✔
403
                }
404
            }
979✔
405

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

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

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

460
    bool try_reloc(const std::string& symbol_name, const ELFIO::Elf_Half symbol_section_index,
93,166✔
461
                   std::vector<ebpf_inst>& instructions, const size_t location, const ELFIO::Elf_Word index) {
462
        ebpf_inst& instruction_to_relocate = instructions[location];
93,166✔
463

464
        // Queue up relocation for function symbols.
465
        if (instruction_to_relocate.opcode == INST_OP_CALL && instruction_to_relocate.src == INST_CALL_LOCAL) {
93,166✔
466
            function_relocations.emplace_back(function_relocation{
14,701✔
467
                .prog_index = raw_programs.size(),
14,701✔
468
                .source_offset = location,
469
                .relocation_entry_index = index,
14,701✔
470
                .target_function_name = symbol_name,
471
            });
472
            return true;
14,701✔
473
        }
474

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

481
        // Perform relocation for symbols located in the maps section.
482
        if (global.map_section_indices.contains(symbol_section_index)) {
78,465✔
483
            instruction_to_relocate.src = INST_LD_MODE_MAP_FD;
78,461✔
484
            instruction_to_relocate.imm = relocate_map(symbol_name, index);
78,461✔
485
            return true;
78,461✔
486
        }
487

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

502
    void process_relocations(std::vector<ebpf_inst>& instructions,
3,338✔
503
                             const ELFIO::const_relocation_section_accessor& reloc, const std::string& section_name,
504
                             const ELFIO::Elf_Xword program_offset, const size_t program_size) {
505
        for (ELFIO::Elf_Xword i = 0; i < reloc.get_entries_num(); i++) {
96,530✔
506
            ELFIO::Elf64_Addr offset{};
93,192✔
507
            ELFIO::Elf_Word index{};
93,192✔
508
            unsigned type{};
93,192✔
509
            ELFIO::Elf_Sxword addend{};
93,192✔
510
            if (reloc.get_entry(i, offset, index, type, addend)) {
93,192✔
511
                if (offset < program_offset || offset >= program_offset + program_size) {
93,192✔
512
                    // Relocation is not for this program.
513
                    continue;
26✔
514
                }
515
                offset -= program_offset;
93,166✔
516
                const unsigned long location = offset / sizeof(ebpf_inst);
93,166✔
517
                if (location >= instructions.size()) {
93,166✔
518
                    throw UnmarshalError("Invalid relocation data");
×
519
                }
520

521
                const auto symbol_details = get_symbol_details(symbols, index);
93,166✔
522
                if (!try_reloc(symbol_details.name, symbol_details.section_index, instructions, location, index)) {
93,166✔
523
                    unresolved_symbol_errors.push_back("Unresolved external symbol " + symbol_details.name +
×
524
                                                       " in section " + section_name + " at location " +
×
525
                                                       std::to_string(location));
×
526
                }
527
            }
93,166✔
528
        }
529
    }
3,338✔
530

531
    const ELFIO::section* get_relocation_section(const std::string& section_name) const {
3,558✔
532
        const ELFIO::section* prelocs = reader.sections[".rel" + section_name];
3,558✔
533
        if (!prelocs) {
3,558✔
534
            prelocs = reader.sections[".rela" + section_name];
535✔
535
        }
536
        if (!prelocs) {
535✔
537
            return nullptr;
538
        }
539
        if (!prelocs->get_data()) {
3,338✔
540
            throw UnmarshalError("Malformed relocation data");
×
541
        }
542
        return prelocs;
543
    }
544

545
    void read_programs() {
376✔
546
        for (const auto& section : reader.sections) {
12,100✔
547
            if (!(section->get_flags() & ELFIO::SHF_EXECINSTR)) {
11,724✔
548
                // Section does not contain executable instructions.
549
                continue;
8,189✔
550
            }
551
            const auto section_size = section->get_size();
3,869✔
552
            if (section_size == 0) {
3,869✔
553
                continue;
334✔
554
            }
555
            const auto section_data = section->get_data();
3,535✔
556
            if (section_data == nullptr) {
3,535✔
557
                continue;
×
558
            }
559
            const std::string section_name = section->get_name();
3,535✔
560
            const EbpfProgramType program_type =
3,535✔
561
                parse_params.platform->get_program_type(section_name, parse_params.path);
3,535✔
562

563
            for (ELFIO::Elf_Xword program_offset = 0; program_offset < section_size;) {
7,093✔
564
                auto [program_name, program_size] = get_program_name_and_size(*section, program_offset, symbols);
3,558✔
565
                std::vector<ebpf_inst> instructions = vector_of<ebpf_inst>(section_data + program_offset, program_size);
3,558✔
566

567
                if (const ELFIO::section* reloc_section = get_relocation_section(section_name)) {
3,558✔
568
                    const ELFIO::const_relocation_section_accessor reloc{reader, reloc_section};
3,338✔
569
                    process_relocations(instructions, reloc, section_name, program_offset, program_size);
3,338✔
570
                }
571

572
                raw_programs.emplace_back(raw_program{
3,558✔
573
                    parse_params.path,
3,558✔
574
                    section_name,
575
                    gsl::narrow_cast<uint32_t>(program_offset),
3,558✔
576
                    program_name,
577
                    std::move(instructions),
578
                    program_info{
579
                        .platform = parse_params.platform,
3,558✔
580
                        .map_descriptors = global.map_descriptors,
3,558✔
581
                        .type = program_type,
582
                    },
583
                });
584
                program_offset += program_size;
3,558✔
585
            }
3,558✔
586
        }
3,535✔
587

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

598
        if (const auto btf_section = reader.sections[".BTF"]) {
376✔
599
            if (const auto btf_ext = reader.sections[".BTF.ext"]) {
93✔
600
                update_line_info(raw_programs, btf_section, btf_ext);
93✔
601
            }
602
        }
603

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

617
        // Now that we've incorporated any subprograms from other sections, we can narrow the list
618
        // to return to just those programs in the desired section, if any.
619
        if (!parse_params.desired_section.empty() && !raw_programs.empty()) {
375✔
620
            for (int index = raw_programs.size() - 1; index >= 0; index--) {
3,892✔
621
                if (raw_programs[index].section_name != parse_params.desired_section) {
3,536✔
622
                    raw_programs.erase(raw_programs.begin() + index);
3,159✔
623
                }
624
            }
625
        }
626

627
        if (raw_programs.empty()) {
375✔
628
            if (parse_params.desired_section.empty()) {
1✔
629
                throw UnmarshalError("Can't find any non-empty TEXT sections in file " + parse_params.path);
×
630
            }
631
            throw UnmarshalError("Can't find section " + parse_params.desired_section + " in file " +
2✔
632
                                 parse_params.path);
3✔
633
        }
634
    }
374✔
635
};
636

637
std::vector<raw_program> read_elf(std::istream& input_stream, const std::string& path,
377✔
638
                                  const std::string& desired_section, const ebpf_verifier_options_t& options,
639
                                  const ebpf_platform_t* platform) {
640
    const parse_params_t parse_params{
377✔
641
        .path = path, .options = options, .platform = platform, .desired_section = desired_section};
377✔
642
    const ELFIO::elfio reader = load_elf(input_stream, path);
377✔
643
    const ELFIO::const_symbol_section_accessor symbols = read_and_validate_symbol_section(reader, path);
377✔
644
    const elf_global_data global = extract_global_data(parse_params, reader, symbols);
376✔
645
    program_reader_t program_reader{parse_params, reader, symbols, global};
376✔
646
    program_reader.read_programs();
376✔
647
    return std::move(program_reader.raw_programs);
374✔
648
}
381✔
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

© 2025 Coveralls, Inc