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

vbpf / ebpf-verifier / 12951530094

24 Jan 2025 02:26PM UTC coverage: 88.133% (-0.04%) from 88.169%
12951530094

push

github

web-flow
add support for 64bit immediate with type 2 (#820)

Add support for 64bit immediate with type 2

From the ISA RFC:
5.4.  64-bit immediate instructions

   Instructions with the IMM 'mode' modifier use the wide instruction
   encoding defined in Instruction encoding (Section 3), and use the
   'src_reg' field of the basic instruction to hold an opcode subtype.

   The following table defines a set of {IMM, DW, LD} instructions with
   opcode subtypes in the 'src_reg' field, using new terms such as "map"
   defined further below:

    +=========+================================+==========+==========+
    | src_reg | pseudocode                     | imm type | dst type |
    +=========+================================+==========+==========+
    | 0x0     | dst = (next_imm << 32) | imm   | integer  | integer  |
    +---------+--------------------------------+----------+----------+
    | 0x1     | dst = map_by_fd(imm)           | map fd   | map      |
    +---------+--------------------------------+----------+----------+
    | 0x2     | dst = map_val(map_by_fd(imm))  | map fd   | data     |
    |         | + next_imm                     |          | address  |
    +---------+--------------------------------+----------+----------+
    | 0x3     | dst = var_addr(imm)            | variable | data     |
    |         |                                | id       | address  |
    +---------+--------------------------------+----------+----------+
    | 0x4     | dst = code_addr(imm)           | integer  | code     |
    |         |                                |          | address  |
    +---------+--------------------------------+----------+----------+
    | 0x5     | dst = map_by_idx(imm)          | map      | map      |
    |         |                                | index    |          |
    +---------+--------------------------------+----------+----------+
    | 0x6     | dst = map_val(map_by_idx(imm)) | map      | data     |
    |         | + next_imm    ... (continued)

81 of 92 new or added lines in 12 files covered. (88.04%)

7 existing lines in 1 file now uncovered.

8533 of 9682 relevant lines covered (88.13%)

7382636.56 hits per line

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

88.46
/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.h"
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
using std::string;
21
using std::vector;
22

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

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

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

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

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

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

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

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

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

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

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

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

165
void verify_load_instruction(const ebpf_inst& instruction, const std::string& symbol_name, ELFIO::Elf64_Addr offset) {
313,860✔
166
    if ((instruction.opcode & INST_CLS_MASK) != INST_CLS_LD) {
313,860✔
NEW
167
        throw UnmarshalError("Illegal operation on symbol " + symbol_name + " at location " +
×
NEW
168
                             std::to_string(offset / sizeof(ebpf_inst)));
×
169
    }
170
}
313,860✔
171

172
void relocate_map(ebpf_inst& reloc_inst, const std::string& symbol_name,
156,922✔
173
                  const std::variant<size_t, std::map<std::string, size_t>>& map_record_size_or_map_offsets,
174
                  const program_info& info, const ELFIO::Elf64_Addr offset, const ELFIO::Elf_Word index,
175
                  const ELFIO::const_symbol_section_accessor& symbols) {
176
    // Only permit loading the address of the map.
177
    verify_load_instruction(reloc_inst, symbol_name, offset);
156,922✔
178
    reloc_inst.src = INST_LD_MODE_MAP_FD;
156,922✔
179

180
    // Relocation value is an offset into the "maps" or ".maps" section.
181
    size_t reloc_value = std::numeric_limits<size_t>::max();
156,922✔
182
    if (map_record_size_or_map_offsets.index() == 0) {
156,922✔
183
        // The older maps section format uses a single map_record_size value, so we can
184
        // calculate the map descriptor index directly.
185
        auto [relocation_offset, relocation_type] = get_value(symbols, index);
156,838✔
186
        reloc_value = relocation_offset / std::get<0>(map_record_size_or_map_offsets);
156,838✔
187
    } else {
188
        // The newer .maps section format uses a variable-length map descriptor array,
189
        // so we need to look up the map descriptor index in a map.
190
        auto& map_descriptors_offsets = std::get<1>(map_record_size_or_map_offsets);
84✔
191
        const auto it = map_descriptors_offsets.find(symbol_name);
84✔
192
        if (it != map_descriptors_offsets.end()) {
84✔
193
            reloc_value = it->second;
84✔
194
        } else {
NEW
195
            throw UnmarshalError("Map descriptor not found for symbol " + symbol_name);
×
196
        }
197
    }
198
    if (reloc_value >= info.map_descriptors.size()) {
156,922✔
199
        throw UnmarshalError("Bad reloc value (" + std::to_string(reloc_value) + "). " +
×
200
                             "Make sure to compile with -O2.");
×
201
    }
202
    reloc_inst.imm = info.map_descriptors.at(reloc_value).original_fd;
156,922✔
203
}
156,922✔
204

205
void relocate_global_variable(ebpf_inst& reloc_inst, ebpf_inst& next_reloc_inst, const std::string& symbol_name,
8✔
206
                              const program_info& info,
207
                              const std::variant<size_t, std::map<std::string, size_t>>& map_record_size_or_map_offsets,
208
                              const ELFIO::Elf64_Addr offset) {
209
    // Only permit loading the address of the global variable.
210
    verify_load_instruction(reloc_inst, symbol_name, offset);
8✔
211

212
    // Copy the immediate value to the next instruction.
213
    next_reloc_inst.imm = reloc_inst.imm;
8✔
214
    reloc_inst.src = INST_LD_MODE_MAP_VALUE;
8✔
215

216
    size_t reloc_value = std::numeric_limits<size_t>::max();
8✔
217
    auto& map_descriptors_offsets = std::get<1>(map_record_size_or_map_offsets);
8✔
218
    const auto it = map_descriptors_offsets.find(symbol_name);
8✔
219
    if (it != map_descriptors_offsets.end()) {
8✔
220
        reloc_value = it->second;
8✔
221
    } else {
NEW
222
        throw UnmarshalError("Map descriptor not found for symbol " + symbol_name);
×
223
    }
224

225
    if (reloc_value >= info.map_descriptors.size()) {
8✔
NEW
226
        throw UnmarshalError("Bad reloc value (" + std::to_string(reloc_value) + "). " +
×
NEW
227
                             "Make sure to compile with -O2.");
×
228
    }
229
    reloc_inst.imm = info.map_descriptors.at(reloc_value).original_fd;
8✔
230
}
8✔
231

232
// Structure used to keep track of subprogram relocation data until any subprograms
233
// are loaded and can be appended to the calling program.
234
struct function_relocation {
78,548✔
235
    size_t prog_index{};              // Index of source program in vector of raw programs.
236
    ELFIO::Elf_Xword source_offset{}; // Instruction offset in source section of source instruction.
237
    ELFIO::Elf_Xword relocation_entry_index{};
238
    string target_function_name;
239
};
240

241
static raw_program* find_subprogram(vector<raw_program>& programs, const ELFIO::section& subprogram_section,
1,958✔
242
                                    const std::string& symbol_name) {
243
    // Find subprogram by name.
244
    for (auto& subprog : programs) {
186,646✔
245
        if ((subprog.section_name == subprogram_section.get_name()) && (subprog.function_name == symbol_name)) {
277,050✔
246
            return &subprog;
12✔
247
        }
248
    }
249
    return nullptr;
973✔
250
}
251

252
// Returns an error message, or empty string on success.
253
static std::string append_subprograms(raw_program& prog, vector<raw_program>& programs,
7,128✔
254
                                      const vector<function_relocation>& function_relocations,
255
                                      const ELFIO::elfio& reader, const ELFIO::const_symbol_section_accessor& symbols) {
256
    if (prog.resolved_subprograms) {
7,128✔
257
        // We've already appended any relevant subprograms.
258
        return {};
12✔
259
    }
260
    prog.resolved_subprograms = true;
7,116✔
261

262
    // Perform function relocations and fill in the inst.imm values of CallLocal instructions.
263
    std::map<string, ELFIO::Elf_Xword> subprogram_offsets;
7,116✔
264
    for (const auto& reloc : function_relocations) {
1,880,892✔
265
        if (reloc.prog_index >= programs.size()) {
1,875,722✔
266
            continue;
1,873,764✔
267
        }
268
        if (programs[reloc.prog_index].function_name != prog.function_name) {
1,875,722✔
269
            continue;
1,873,752✔
270
        }
271

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

276
            auto [symbol_name, section_index] =
1,958✔
277
                get_symbol_name_and_section_index(symbols, reloc.relocation_entry_index);
1,958✔
278
            if (section_index >= reader.sections.size()) {
1,958✔
279
                throw UnmarshalError("Invalid section index " + std::to_string(section_index) + " at source offset " +
×
280
                                     std::to_string(reloc.source_offset));
×
281
            }
282
            ELFIO::section& subprogram_section = *reader.sections[section_index];
1,958✔
283

284
            if (auto subprogram = find_subprogram(programs, subprogram_section, symbol_name)) {
1,958✔
285
                // Make sure subprogram has already had any subprograms of its own appended.
286
                std::string error = append_subprograms(*subprogram, programs, function_relocations, reader, symbols);
12✔
287
                if (!error.empty()) {
12✔
288
                    return error;
×
289
                }
290

291
                // Append subprogram to program.
292
                prog.prog.insert(prog.prog.end(), subprogram->prog.begin(), subprogram->prog.end());
12✔
293
                for (int i = 0; i < subprogram->info.line_info.size(); i++) {
216✔
294
                    prog.info.line_info[prog.info.line_info.size()] = subprogram->info.line_info[i];
204✔
295
                }
296
            } else {
12✔
297
                // The program will be invalid, but continue rather than throwing an exception
298
                // since we might be verifying a different program in the file.
299
                return std::string("Subprogram '" + symbol_name + "' not found in section '" +
4,865✔
300
                                   subprogram_section.get_name() + "'");
5,838✔
301
            }
302
        }
1,958✔
303

304
        // Fill in the PC offset into the imm field of the CallLocal instruction.
305
        const int64_t target_offset = gsl::narrow_cast<int64_t>(subprogram_offsets[reloc.target_function_name]);
24✔
306
        const auto offset_diff = target_offset - gsl::narrow<int64_t>(reloc.source_offset) - 1;
24✔
307
        if (offset_diff < std::numeric_limits<int32_t>::min() || offset_diff > std::numeric_limits<int32_t>::max()) {
24✔
308
            throw UnmarshalError("Offset difference out of int32_t range for instruction at source offset " +
×
309
                                 std::to_string(reloc.source_offset));
×
310
        }
311
        prog.prog[reloc.source_offset].imm = gsl::narrow_cast<int32_t>(offset_diff);
24✔
312
    }
313
    return {};
5,170✔
314
}
3,558✔
315

316
std::map<std::string, size_t> parse_map_section(const libbtf::btf_type_data& btf_data,
116✔
317
                                                std::vector<EbpfMapDescriptor>& map_descriptors) {
318
    std::map<std::string, size_t> map_offsets;
116✔
319
    for (const auto& map : parse_btf_map_section(btf_data)) {
208✔
320
        map_offsets.emplace(map.name, map_descriptors.size());
92✔
321
        map_descriptors.push_back({
92✔
322
            .original_fd = gsl::narrow_cast<int>(map.type_id),
92✔
323
            .type = map.map_type,
92✔
324
            .key_size = map.key_size,
92✔
325
            .value_size = map.value_size,
92✔
326
            .max_entries = map.max_entries,
92✔
327
            .inner_map_fd = map.inner_map_type_id != 0 ? map.inner_map_type_id : DEFAULT_MAP_FD,
92✔
328
        });
329
    }
116✔
330
    return map_offsets;
116✔
331
}
×
332

333
vector<raw_program> read_elf(std::istream& input_stream, const std::string& path, const std::string& desired_section,
754✔
334
                             const ebpf_verifier_options_t& options, const ebpf_platform_t* platform) {
335
    ELFIO::elfio reader;
754✔
336
    if (!reader.load(input_stream)) {
754✔
337
        throw UnmarshalError("Can't process ELF file " + path);
×
338
    }
339

340
    auto symbol_section = reader.sections[".symtab"];
754✔
341
    if (!symbol_section) {
754✔
342
        throw UnmarshalError("No symbol section found in ELF file " + path);
×
343
    }
344

345
    // Make sure the ELFIO library will be able to parse the symbol section correctly.
346
    auto expected_entry_size =
377✔
347
        reader.get_class() == ELFIO::ELFCLASS32 ? sizeof(ELFIO::Elf32_Sym) : sizeof(ELFIO::Elf64_Sym);
754✔
348

349
    if (symbol_section->get_entry_size() != expected_entry_size) {
754✔
350
        throw UnmarshalError("Invalid symbol section found in ELF file " + path);
3✔
351
    }
352

353
    program_info info{platform};
752✔
354
    std::set<ELFIO::Elf_Half> map_section_indices;
752✔
355
    std::set<ELFIO::Elf_Half> global_variable_section_indices;
752✔
356

357
    auto btf = reader.sections[".BTF"];
754✔
358
    std::optional<libbtf::btf_type_data> btf_data;
752✔
359

360
    if (btf) {
752✔
361
        // Parse the BTF type data.
362
        btf_data = vector_of<std::byte>(*btf);
188✔
363
        if (options.verbosity_opts.dump_btf_types_json) {
186✔
364
            std::stringstream output;
×
365
            std::cout << "Dumping BTF data for" << path << std::endl;
×
366
            // Dump the BTF data to cout for debugging purposes.
367
            btf_data->to_json(output);
×
368
            std::cout << libbtf::pretty_print_json(output.str()) << std::endl;
×
369
            std::cout << std::endl;
×
370
        }
×
371
    }
372

373
    std::variant<size_t, std::map<std::string, size_t>> map_record_size_or_map_offsets = size_t{0};
752✔
374
    ELFIO::const_symbol_section_accessor symbols{reader, symbol_section};
752✔
375

376
    if (std::ranges::any_of(reader.sections, [](const auto& section) { return is_map_section(section->get_name()); })) {
19,738✔
377
        map_record_size_or_map_offsets =
282✔
378
            parse_map_sections(options, platform, reader, info.map_descriptors, map_section_indices, symbols);
564✔
379
    } else if (btf_data.has_value()) {
188✔
380
        map_record_size_or_map_offsets = parse_map_section(*btf_data, info.map_descriptors);
118✔
381
        // Prevail requires:
382
        // Map fds are sequential starting from 1.
383
        // Map fds are assigned in the order of the maps in the ELF file.
384

385
        // Build a map from the original type id to the pseudo-fd.
386
        std::map<int, int> type_id_to_fd_map;
116✔
387
        int pseudo_fd = 1;
116✔
388
        // Gather the typeids for each map and assign a pseudo-fd to each map.
389
        for (const auto& map_descriptor : info.map_descriptors) {
208✔
390
            if (!type_id_to_fd_map.contains(map_descriptor.original_fd)) {
92✔
391
                type_id_to_fd_map[map_descriptor.original_fd] = pseudo_fd++;
92✔
392
            }
393
        }
394
        // Replace the typeids with the pseudo-fds.
395
        for (auto& map_descriptor : info.map_descriptors) {
208✔
396
            map_descriptor.original_fd = type_id_to_fd_map[map_descriptor.original_fd];
92✔
397
            if (map_descriptor.inner_map_fd != DEFAULT_MAP_FD) {
92✔
398
                map_descriptor.inner_map_fd = type_id_to_fd_map[map_descriptor.inner_map_fd];
6✔
399
            }
400
        }
401
        if (reader.sections[".maps"]) {
116✔
402
            map_section_indices.insert(reader.sections[".maps"]->get_index());
74✔
403
        }
404

405
        for (auto section_name : {".rodata", ".data", ".bss"}) {
464✔
406
            if (const auto section = reader.sections[section_name]) {
348✔
407
                if (section->get_size() != 0) {
6✔
408
                    global_variable_section_indices.insert(section->get_index());
6✔
409
                }
410
            }
411
        }
412
    }
116✔
413

414
    vector<raw_program> res;
752✔
415
    vector<string> unresolved_symbols_errors;
752✔
416
    vector<function_relocation> function_relocations;
752✔
417
    for (const auto& section : reader.sections) {
24,200✔
418
        if (!(section->get_flags() & ELFIO::SHF_EXECINSTR)) {
23,448✔
419
            // Section does not contain executable instructions.
420
            continue;
16,378✔
421
        }
422
        const auto section_size = section->get_size();
7,738✔
423
        if (section_size == 0) {
7,738✔
424
            continue;
668✔
425
        }
426
        const auto section_data = section->get_data();
7,070✔
427
        if (section_data == nullptr) {
7,070✔
428
            continue;
×
429
        }
430
        const string name = section->get_name();
7,070✔
431
        info.type = platform->get_program_type(name, path);
7,070✔
432

433
        for (ELFIO::Elf_Xword program_offset = 0; program_offset < section_size;) {
14,186✔
434
            auto [program_name, program_size] = get_program_name_and_size(*section, program_offset, symbols);
7,116✔
435
            raw_program prog{path,
7,116✔
436
                             name,
437
                             gsl::narrow_cast<uint32_t>(program_offset),
7,116✔
438
                             program_name,
439
                             vector_of<ebpf_inst>(section_data + program_offset, program_size),
3,558✔
440
                             info};
7,116✔
441

442
            auto prelocs = reader.sections[".rel" + name];
7,116✔
443
            if (!prelocs) {
7,116✔
444
                prelocs = reader.sections[".rela" + name];
1,070✔
445
            }
446

447
            if (prelocs) {
4,093✔
448
                if (!prelocs->get_data()) {
6,676✔
449
                    throw UnmarshalError("Malformed relocation data");
×
450
                }
451
                ELFIO::const_relocation_section_accessor reloc{reader, prelocs};
6,676✔
452

453
                // Fetch and store relocation count locally to permit static
454
                // analysis tools to correctly reason about the code below.
455
                for (ELFIO::Elf_Xword i = 0; i < reloc.get_entries_num(); i++) {
193,060✔
456
                    ELFIO::Elf64_Addr offset{};
186,384✔
457
                    ELFIO::Elf_Word index{};
186,384✔
458
                    unsigned type{};
186,384✔
459
                    ELFIO::Elf_Sxword addend{};
186,384✔
460
                    if (!reloc.get_entry(i, offset, index, type, addend)) {
186,384✔
461
                        continue;
186,384✔
462
                    }
463
                    if (offset < program_offset || offset >= program_offset + program_size) {
186,384✔
464
                        // Relocation is not for this program.
465
                        continue;
52✔
466
                    }
467
                    offset -= program_offset;
186,332✔
468
                    if (offset / sizeof(ebpf_inst) >= prog.prog.size()) {
186,332✔
469
                        throw UnmarshalError("Invalid relocation data");
×
470
                    }
471

472
                    ebpf_inst& reloc_inst = prog.prog[offset / sizeof(ebpf_inst)];
186,332✔
473

474
                    auto [symbol_name, symbol_section_index] = get_symbol_name_and_section_index(symbols, index);
186,332✔
475

476
                    // Queue up relocation for function symbols.
477
                    if (reloc_inst.opcode == INST_OP_CALL && reloc_inst.src == INST_CALL_LOCAL) {
186,332✔
478
                        function_relocation fr{.prog_index = res.size(),
29,402✔
479
                                               .source_offset = offset / sizeof(ebpf_inst),
14,701✔
480
                                               .relocation_entry_index = index,
14,701✔
481
                                               .target_function_name = symbol_name};
29,402✔
482
                        function_relocations.push_back(fr);
29,402✔
483
                        continue;
29,402✔
484
                    }
29,402✔
485

486
                    // Verify that this is a map or global variable relocation.
487
                    verify_load_instruction(reloc_inst, symbol_name, offset);
156,930✔
488

489
                    // Load instructions are two instructions long, so we need to check the next instruction.
490
                    if (prog.prog.size() <= offset / sizeof(ebpf_inst) + 1) {
156,930✔
NEW
491
                        throw UnmarshalError("Invalid relocation data");
×
492
                    }
493
                    ebpf_inst& next_reloc_inst = prog.prog[offset / sizeof(ebpf_inst) + 1];
156,930✔
494

495
                    // Perform relocation for symbols located in the maps section.
496
                    if (map_section_indices.contains(symbol_section_index)) {
156,930✔
497
                        relocate_map(reloc_inst, symbol_name, map_record_size_or_map_offsets, info, offset, index,
156,922✔
498
                                     symbols);
499
                        continue;
156,922✔
500
                    }
501

502
                    if (global_variable_section_indices.contains(symbol_section_index)) {
8✔
503
                        relocate_global_variable(reloc_inst, next_reloc_inst,
8✔
504
                                                 reader.sections[symbol_section_index]->get_name(), info,
16✔
505
                                                 map_record_size_or_map_offsets, offset);
506
                        continue;
8✔
507
                    }
508

UNCOV
509
                    string unresolved_symbol = "Unresolved external symbol " + symbol_name + " in section " + name +
×
UNCOV
510
                                               " at location " + std::to_string(offset / sizeof(ebpf_inst));
×
UNCOV
511
                    unresolved_symbols_errors.push_back(unresolved_symbol);
×
512
                }
186,332✔
513
            }
514
            res.push_back(prog);
7,116✔
515
            program_offset += program_size;
7,116✔
516
        }
7,116✔
517
    }
7,070✔
518

519
    // Below, only relocations of symbols located in the map sections are allowed,
520
    // so if there are relocations there needs to be a maps section.
521
    if (!unresolved_symbols_errors.empty()) {
752✔
UNCOV
522
        for (const auto& unresolved_symbol : unresolved_symbols_errors) {
×
UNCOV
523
            std::cerr << unresolved_symbol << std::endl;
×
524
        }
UNCOV
525
        throw UnmarshalError("There are relocations in section but no maps sections in file " + path +
×
UNCOV
526
                             "\nMake sure to inline all function calls.");
×
527
    }
528

529
    auto btf_ext = reader.sections[".BTF.ext"];
752✔
530
    if (btf && btf_ext) {
752✔
531
        auto visitor = [&](const string& section, const uint32_t instruction_offset, const string& file_name,
1,256,823✔
532
                           const string& source, const uint32_t line_number, const uint32_t column_number) {
533
            for (auto& program : res) {
61,377,566✔
534
                if (program.section_name == section && instruction_offset >= program.insn_off &&
60,749,352✔
535
                    instruction_offset < program.insn_off + program.prog.size() * sizeof(ebpf_inst)) {
1,257,032✔
536
                    const size_t inst_index = (instruction_offset - program.insn_off) / sizeof(ebpf_inst);
1,256,730✔
537
                    if (inst_index >= program.prog.size()) {
1,256,730✔
538
                        throw UnmarshalError("Invalid BTF data");
×
539
                    }
540
                    program.info.line_info.insert_or_assign(
628,365✔
541
                        inst_index, btf_line_info_t{file_name, source, line_number, column_number});
1,885,095✔
542
                }
543
            }
544
        };
1,256,730✔
545

546
        libbtf::btf_parse_line_information(vector_of<std::byte>(*btf), vector_of<std::byte>(*btf_ext), visitor);
279✔
547

548
        // BTF doesn't include line info for every instruction, only on the first instruction per source line.
549
        for (auto& program : res) {
3,154✔
550
            for (size_t i = 1; i < program.prog.size(); i++) {
4,884,832✔
551
                // If the previous PC has line info, copy it.
552
                if (program.info.line_info[i].line_number == 0 && program.info.line_info[i - 1].line_number != 0) {
4,881,864✔
553
                    program.info.line_info[i] = program.info.line_info[i - 1];
3,782,354✔
554
                }
555
            }
556
        }
557
    }
558

559
    // Now that we have all programs in the list, we can recursively append any subprograms
560
    // to the calling programs.  We have to keep them as programs themselves in case the caller
561
    // wants to verify them separately, but we also have to append them if used as subprograms to
562
    // allow the caller to be fully verified since inst.imm can only point into the same program.
563
    for (auto& prog : res) {
7,866✔
564
        std::string error = append_subprograms(prog, res, function_relocations, reader, symbols);
7,116✔
565
        if (!error.empty()) {
7,116✔
566
            if (prog.section_name == desired_section) {
1,946✔
567
                throw UnmarshalError(error);
2✔
568
            }
569
        }
570
    }
7,116✔
571

572
    // Now that we've incorporated any subprograms from other sections, we can narrow the list
573
    // to return to just those programs in the desired section, if any.
574
    if (!desired_section.empty() && !res.empty()) {
750✔
575
        for (int index = res.size() - 1; index >= 0; index--) {
7,784✔
576
            if (res[index].section_name != desired_section) {
7,072✔
577
                res.erase(res.begin() + index);
6,318✔
578
            }
579
        }
580
    }
581

582
    if (res.empty()) {
750✔
583
        if (desired_section.empty()) {
2✔
584
            throw UnmarshalError("Can't find any non-empty TEXT sections in file " + path);
×
585
        }
586
        throw UnmarshalError("Can't find section " + desired_section + " in file " + path);
4✔
587
    }
588
    return res;
1,122✔
589
}
784✔
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