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

Alan-Jowett / libbtf / 28060332906

31 Mar 2026 06:26PM UTC coverage: 92.773% (-2.3%) from 95.029%
28060332906

push

github

web-flow
Merge pull request #194 from Alan-Jowett/dependabot/github_actions/actions-f78bc712d6

Bump the actions group with 3 updates

2285 of 2463 relevant lines covered (92.77%)

768.8 hits per line

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

91.63
/libbtf/btf_map.cpp
1
// Copyright (c) Prevail Verifier contributors.
2
// SPDX-License-Identifier: MIT
3

4
#include "btf_map.h"
5

6
#include "btf.h"
7
#include "btf_json.h"
8
#include "btf_parse.h"
9
#include "btf_type_data.h"
10
#include "btf_write.h"
11
#include "cycle_detector.h"
12

13
#include <algorithm>
14
#include <stdexcept>
15

16
namespace libbtf {
17
static uint32_t _value_from_BTF__uint(const btf_type_data &btf_types,
46✔
18
                                      btf_type_id type_id) {
19
  // The __uint macro is defined as follows:
20
  // #define __uint(name, val) int (*name)[val]
21
  // So, we need to get the value of val from the BTF type.
22

23
  // Top level should be a pointer. Dereference it.
24
  type_id = btf_types.dereference_pointer(type_id);
46✔
25

26
  // Value is encoded in the count of elements.
27
  return btf_types.get_kind_type<btf_kind_array>(type_id).count_of_elements;
46✔
28
}
29

30
/**
31
 * @brief Walk the type chain removing typedefs, const, and volatile until any
32
 * other type is found.
33
 *
34
 * @param[in] btf_types The BTF types object.
35
 * @param[in] type_id The type ID to unwrap.
36
 * @return The unwrapped type ID.
37
 */
38
static btf_type_id _unwrap_type(const btf_type_data &btf_types,
25✔
39
                                btf_type_id type_id) {
40
  libbtf::cycle_detector detector;
25✔
41

42
  for (;;) {
43
    // Check for cycles and mark as visited atomically
44
    if (!detector.mark_visited(type_id)) {
29✔
45
      // Cycle detected - return current type_id to break the cycle
46
      return type_id;
×
47
    }
48

49
    switch (btf_types.get_kind_index(type_id)) {
29✔
50
    case BTF_KIND_TYPEDEF:
4✔
51
      type_id = btf_types.get_kind_type<btf_kind_typedef>(type_id).type;
4✔
52
      break;
4✔
53
    case BTF_KIND_CONST:
×
54
      type_id = btf_types.get_kind_type<btf_kind_const>(type_id).type;
×
55
      break;
×
56
    case BTF_KIND_VOLATILE:
×
57
      type_id = btf_types.get_kind_type<btf_kind_volatile>(type_id).type;
×
58
      break;
×
59
    case BTF_KIND_RESTRICT:
×
60
      type_id = btf_types.get_kind_type<btf_kind_restrict>(type_id).type;
×
61
      break;
×
62
    default:
25✔
63
      return type_id;
25✔
64
    }
65
  }
66
}
25✔
67

68
/**
69
 * @brief Check if the given type is a map. This is done by checking if the type
70
 * is a struct with the following members:
71
 * - type
72
 * - max_entries
73
 *
74
 * @param btf_types The BTF types object.
75
 * @param map_type_id The type id of the type to check.
76
 * @return true This is a map type.
77
 * @return false This is not a map type.
78
 */
79
static bool _is_map_type(const btf_type_data &btf_types,
7✔
80
                         btf_type_id map_type_id) {
81
  if (btf_types.get_kind_index(map_type_id) != BTF_KIND_STRUCT) {
7✔
82
    return false;
1✔
83
  }
84

85
  auto map_struct = btf_types.get_kind_type<btf_kind_struct>(map_type_id);
6✔
86
  bool has_type = false;
6✔
87
  bool has_max_entries = false;
6✔
88

89
  for (const auto &member : map_struct.members) {
28✔
90
    if (member.name == "type") {
22✔
91
      has_type = true;
6✔
92
    } else if (member.name == "max_entries") {
16✔
93
      has_max_entries = true;
6✔
94
    }
95
  }
96

97
  return has_type && has_max_entries;
6✔
98
}
6✔
99

100
/**
101
 * @brief Accept a BTF type ID for a map and return a BTF map definition.
102
 *
103
 * @param[in] btf_types The BTF types object.
104
 * @param[in] name The name of the map or empty string if the name is not
105
 * available.
106
 * @param[in] map_type_id The ID of the struct type for the map.
107
 * @return btf_map_definition
108
 */
109
static btf_map_definition
110
_get_map_definition_from_btf(const btf_type_data &btf_types,
18✔
111
                             const std::string &name, btf_type_id map_type_id) {
112
  btf_type_id type = 0;
18✔
113
  btf_type_id max_entries = 0;
18✔
114
  btf_type_id key = 0;
18✔
115
  btf_type_id key_size = 0;
18✔
116
  btf_type_id value = 0;
18✔
117
  btf_type_id value_size = 0;
18✔
118
  btf_type_id values = 0;
18✔
119

120
  map_type_id = _unwrap_type(btf_types, map_type_id);
18✔
121

122
  auto map_struct = btf_types.get_kind_type<btf_kind_struct>(map_type_id);
18✔
123

124
  for (const auto &member : map_struct.members) {
86✔
125
    if (member.name == "type") {
68✔
126
      type = member.type;
18✔
127
    } else if (member.name == "max_entries") {
50✔
128
      max_entries = member.type;
18✔
129
    } else if (member.name == "key") {
32✔
130
      key = btf_types.dereference_pointer(member.type);
11✔
131
    } else if (member.name == "value") {
21✔
132
      value = btf_types.dereference_pointer(member.type);
4✔
133
    } else if (member.name == "key_size") {
17✔
134
      key_size = member.type;
5✔
135
    } else if (member.name == "value_size") {
12✔
136
      value_size = member.type;
5✔
137
    } else if (member.name == "values") {
7✔
138
      values = member.type;
7✔
139
    }
140
  }
141

142
  if (type == 0) {
18✔
143
    throw std::runtime_error("invalid map type");
×
144
  }
145

146
  btf_map_definition map_definition = {};
18✔
147
  map_definition.name = name;
18✔
148

149
  // Required fields.
150
  map_definition.type_id = map_type_id;
18✔
151
  map_definition.map_type = _value_from_BTF__uint(btf_types, type);
18✔
152

153
  // Optional fields.
154
  if (max_entries) {
18✔
155
    map_definition.max_entries = _value_from_BTF__uint(btf_types, max_entries);
18✔
156
  }
157

158
  if (key) {
18✔
159
    size_t key_size_in_bytes = btf_types.get_size(key);
11✔
160
    if (key_size_in_bytes == 0) {
11✔
161
      std::string type_name = btf_types.get_type_name(key);
1✔
162
      if (type_name.empty()) {
1✔
163
        type_name = "type_id=" + std::to_string(key);
×
164
      }
165
      throw std::runtime_error("map '" + name +
3✔
166
                               "': cannot determine key size for type '" +
2✔
167
                               type_name + "'");
4✔
168
    }
1✔
169
    if (key_size_in_bytes > UINT32_MAX) {
10✔
170
      throw std::runtime_error("key size too large");
×
171
    }
172
    map_definition.key_size = static_cast<uint32_t>(key_size_in_bytes);
10✔
173
  } else if (key_size) {
7✔
174
    map_definition.key_size = _value_from_BTF__uint(btf_types, key_size);
5✔
175
  }
176

177
  if (value) {
17✔
178
    size_t value_size_in_bytes = btf_types.get_size(value);
4✔
179
    if (value_size_in_bytes == 0) {
4✔
180
      std::string type_name = btf_types.get_type_name(value);
1✔
181
      if (type_name.empty()) {
1✔
182
        type_name = "type_id=" + std::to_string(value);
×
183
      }
184
      throw std::runtime_error("map '" + name +
3✔
185
                               "': cannot determine value size for type '" +
2✔
186
                               type_name + "'");
4✔
187
    }
1✔
188
    if (value_size_in_bytes > UINT32_MAX) {
3✔
189
      throw std::runtime_error("value size too large");
×
190
    }
191
    map_definition.value_size = static_cast<uint32_t>(value_size_in_bytes);
3✔
192
  } else if (value_size) {
13✔
193
    map_definition.value_size = _value_from_BTF__uint(btf_types, value_size);
5✔
194
  }
195

196
  if (values) {
16✔
197
    // Values is an array of pointers to BTF map definitions.
198
    auto values_array = btf_types.get_kind_type<btf_kind_array>(values);
7✔
199
    auto ptr = btf_types.get_kind_type<btf_kind_ptr>(values_array.element_type);
7✔
200

201
    auto inner_map_type_id = _unwrap_type(btf_types, ptr.type);
7✔
202

203
    if (_is_map_type(btf_types, inner_map_type_id)) {
7✔
204
      // Value is a map.
205
      // Store the inner map type ID and set value size to 4 bytes (the size of
206
      // a map id).
207
      map_definition.inner_map_type_id = static_cast<int>(inner_map_type_id);
6✔
208
      map_definition.value_size = sizeof(uint32_t);
6✔
209
    } else if (btf_types.get_kind_index(inner_map_type_id) ==
1✔
210
               BTF_KIND_FUNCTION_PROTOTYPE) {
211
      // Value is a BPF program.
212
      // Set the value size to 4 bytes (the size of a program id).
213
      map_definition.value_size = sizeof(uint32_t);
1✔
214
    } else {
215
      throw std::runtime_error("invalid type for values");
×
216
    }
217
  }
218

219
  return map_definition;
32✔
220
}
20✔
221

222
std::vector<btf_map_definition>
223
parse_btf_map_section(const btf_type_data &btf_data) {
13✔
224
  std::multimap<btf_type_id, btf_map_definition> map_definitions;
13✔
225

226
  if (btf_data.get_id(".maps") != 0) {
13✔
227
    std::set<btf_type_id> inner_map_type_ids;
12✔
228

229
    // Get the .maps data section.
230
    auto maps_section =
231
        btf_data.get_kind_type<btf_kind_data_section>(btf_data.get_id(".maps"));
24✔
232

233
    // Helper function to add a map definition to the map definitions and add
234
    // the inner map type ID to the list of inner map type IDs if it is present.
235
    auto handle_map_type_id = [&](const std::string &name,
18✔
236
                                  btf_type_id map_type_id) {
237
      auto map_definition =
238
          _get_map_definition_from_btf(btf_data, name, map_type_id);
18✔
239
      map_definitions.insert({map_definition.type_id, map_definition});
16✔
240
      // Add the inner map type ID to the list of inner map type IDs if it is
241
      // present.
242
      if (map_definition.inner_map_type_id != 0) {
16✔
243
        inner_map_type_ids.insert(map_definition.inner_map_type_id);
6✔
244
      }
245
    };
16✔
246

247
    // Add all maps in the .maps data section.
248
    for (const auto &var : maps_section.members) {
26✔
249
      auto map_var = btf_data.get_kind_type<btf_kind_var>(var.type);
16✔
250
      handle_map_type_id(map_var.name, map_var.type);
16✔
251
    }
16✔
252

253
    // Recursively add all inner maps. Assume that there are at most two levels
254
    // of inner maps. This is the current limit imposed by the BPF verifier on
255
    // Linux.
256
    for (size_t inner_map_recursion_level = 0; inner_map_recursion_level < 2;
30✔
257
         inner_map_recursion_level++) {
258
      // Add all maps that are not in the .maps data section.
259
      for (const auto &map_type_id : inner_map_type_ids) {
30✔
260
        // Skip if the map is already present.
261
        if (map_definitions.find(map_type_id) != map_definitions.end()) {
10✔
262
          continue;
8✔
263
        }
264
        handle_map_type_id("", map_type_id);
2✔
265
      }
266
    }
267
  }
14✔
268

269
  // Add an array map for this data section.
270
  auto handle_data_section = [&](btf_type_id data_section_id) {
3✔
271
    auto data_section =
272
        btf_data.get_kind_type<btf_kind_data_section>(data_section_id);
3✔
273
    if (data_section.members.empty()) { // Skip empty data sections.
3✔
274
      return;
×
275
    }
276
    btf_map_definition map_definition = {};
3✔
277
    map_definition.name = data_section.name;
3✔
278
    map_definition.type_id = data_section_id;
3✔
279
    map_definition.key_size = sizeof(uint32_t);
3✔
280
    map_definition.value_size =
3✔
281
        data_section.members.back().offset + data_section.members.back().size;
3✔
282
    map_definition.max_entries = 1;
3✔
283
    map_definitions.insert({map_definition.type_id, map_definition});
3✔
284
  };
3✔
285

286
  // Create a map for .bss, if it exists.
287
  if (btf_data.get_id(".bss") != 0) {
11✔
288
    handle_data_section(btf_data.get_id(".bss"));
1✔
289
  }
290

291
  // Create a map for .data, if it exists.
292
  if (btf_data.get_id(".data") != 0) {
11✔
293
    handle_data_section(btf_data.get_id(".data"));
1✔
294
  }
295

296
  // Create a map for .rodata, if it exists.
297
  if (btf_data.get_id(".rodata") != 0) {
11✔
298
    handle_data_section(btf_data.get_id(".rodata"));
1✔
299
  }
300

301
  std::vector<btf_map_definition> map_definitions_vector;
11✔
302
  for (const auto &map_definition : map_definitions) {
30✔
303
    map_definitions_vector.push_back(map_definition.second);
19✔
304
  }
305
  return map_definitions_vector;
22✔
306
}
13✔
307

308
btf_type_id btf_uint_from_value(btf_type_data &btf_data, uint32_t value) {
20✔
309
  btf_type_id int_id = btf_data.get_id("int");
20✔
310
  if (int_id == 0) {
20✔
311
    int_id = btf_data.append(btf_kind_int{
4✔
312
        .name = "int", .size_in_bytes = 4, .field_width_in_bits = 32});
313
  }
314

315
  btf_type_id array_size_id = btf_data.get_id("__ARRAY_SIZE_TYPE__");
20✔
316
  if (array_size_id == 0) {
20✔
317
    array_size_id = btf_data.append(btf_kind_int{
4✔
318
        .name = "__ARRAY_SIZE_TYPE__",
319
        .size_in_bytes = 4,
320
        .field_width_in_bits = 32,
321
    });
322
  }
323

324
  btf_type_id array = btf_data.append(btf_kind_array{
20✔
325
      .element_type = int_id,
326
      .index_type = array_size_id,
327
      .count_of_elements = value,
328
  });
329

330
  return btf_data.append(btf_kind_ptr{.type = array});
20✔
331
}
332

333
btf_type_id build_btf_map(btf_type_data &btf_data,
5✔
334
                          const btf_map_definition &map_definition) {
335
  uint32_t offset_in_bits = 0;
5✔
336
  btf_kind_struct map{
5✔
337
      .members =
338
          {
339
              {
340
                  .name = "type",
341
                  .type =
342
                      btf_uint_from_value(btf_data, map_definition.map_type),
5✔
343
              },
344
              {
345
                  .name = "max_entries",
346
                  .type =
347
                      btf_uint_from_value(btf_data, map_definition.max_entries),
5✔
348
              },
349
          },
350
  };
25✔
351
  if (map_definition.key_size) {
5✔
352
    map.members.push_back({
10✔
353
        .name = "key_size",
354
        .type = btf_uint_from_value(btf_data, map_definition.key_size),
5✔
355
    });
356
  }
357
  if (map_definition.value_size) {
5✔
358
    map.members.push_back({
10✔
359
        .name = "value_size",
360
        .type = btf_uint_from_value(btf_data, map_definition.value_size),
5✔
361
    });
362
  }
363

364
  if (map_definition.inner_map_type_id != 0) {
5✔
365
    map.members.push_back(
2✔
366
        {.name = "values",
367
         .type = btf_data.append({btf_kind_array{
2✔
368
             .element_type = btf_data.append(
1✔
369
                 btf_kind_ptr{.type = map_definition.inner_map_type_id}),
2✔
370
             .index_type = btf_data.get_id("__ARRAY_SIZE_TYPE__"),
2✔
371
         }})});
372
  }
373

374
  for (auto &member : map.members) {
26✔
375
    member.offset_from_start_in_bits = offset_in_bits;
21✔
376
    offset_in_bits += static_cast<uint32_t>(btf_data.get_size(member.type) * 8);
21✔
377
  }
378

379
  map.size_in_bytes = offset_in_bits / 8;
5✔
380

381
  return btf_data.append(btf_kind_var{
10✔
382
      .name = map_definition.name,
5✔
383
      .type = btf_data.append(map),
10✔
384
      .linkage = BTF_LINKAGE_STATIC,
385
  });
10✔
386
}
5✔
387

388
void build_btf_map_section(
4✔
389
    const std::vector<btf_map_definition> &map_definitions,
390
    btf_type_data &btf_data) {
391
  btf_kind_data_section maps_section{.name = ".maps"};
4✔
392
  std::map<btf_type_id, btf_type_id> old_id_to_new_id;
4✔
393

394
  for (auto &map_definition : map_definitions) {
9✔
395
    btf_type_id var_id = build_btf_map(btf_data, map_definition);
5✔
396
    btf_type_id map_id = btf_data.get_kind_type<btf_kind_var>(var_id).type;
5✔
397
    maps_section.members.push_back(btf_kind_data_member{
×
398
        .type = var_id,
399
        .size = static_cast<uint32_t>(btf_data.get_size(map_id)),
5✔
400
    });
401
    old_id_to_new_id[map_definition.type_id] = map_id;
5✔
402
  }
403

404
  // Update the map inner_map_type_id in the BTF types.
405
  for (auto &map_definition : map_definitions) {
9✔
406
    if (map_definition.inner_map_type_id == 0) {
5✔
407
      continue;
4✔
408
    }
409
    auto new_id = old_id_to_new_id[map_definition.type_id];
1✔
410
    auto ptr_id = btf_data
411
                      .get_kind_type<btf_kind_array>(
1✔
412
                          btf_data.get_kind_type<btf_kind_struct>(new_id)
1✔
413
                              .members.back()
1✔
414
                              .type)
415
                      .element_type;
1✔
416

417
    auto ptr = btf_data.get_kind_type<btf_kind_ptr>(ptr_id);
1✔
418
    ptr.type = old_id_to_new_id[ptr.type];
1✔
419

420
    btf_data.replace(ptr_id, ptr);
1✔
421
  }
422

423
  btf_data.append(maps_section);
4✔
424
  return;
8✔
425
}
4✔
426

427
} // namespace libbtf
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc