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

Alan-Jowett / libbtf / 18689846106

21 Oct 2025 03:54PM UTC coverage: 95.996% (+0.7%) from 95.331%
18689846106

Pull #173

github

web-flow
Merge 765c6cca4 into 643757eaf
Pull Request #173: Add support for correctly handling BTF data with cycles

890 of 935 new or added lines in 6 files covered. (95.19%)

1 existing line in 1 file now uncovered.

2182 of 2273 relevant lines covered (96.0%)

1576.27 hits per line

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

93.88
/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,
84✔
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);
84✔
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;
84✔
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,
46✔
39
                                btf_type_id type_id) {
40
  libbtf::cycle_detector detector;
23✔
41

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

49
    switch (btf_types.get_kind_index(type_id)) {
54✔
50
    case BTF_KIND_TYPEDEF:
8✔
51
      type_id = btf_types.get_kind_type<btf_kind_typedef>(type_id).type;
8✔
52
      break;
8✔
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;
×
NEW
59
    case BTF_KIND_RESTRICT:
×
NEW
60
      type_id = btf_types.get_kind_type<btf_kind_restrict>(type_id).type;
×
NEW
61
      break;
×
62
    default:
23✔
63
      return type_id;
23✔
64
    }
65
  }
66
}
23✔
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,
14✔
80
                         btf_type_id map_type_id) {
81
  if (btf_types.get_kind_index(map_type_id) != BTF_KIND_STRUCT) {
14✔
82
    return false;
1✔
83
  }
84

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

89
  for (const auto &member : map_struct.members) {
56✔
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;
12✔
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,
32✔
111
                             const std::string &name, btf_type_id map_type_id) {
112
  btf_type_id type = 0;
16✔
113
  btf_type_id max_entries = 0;
16✔
114
  btf_type_id key = 0;
16✔
115
  btf_type_id key_size = 0;
16✔
116
  btf_type_id value = 0;
16✔
117
  btf_type_id value_size = 0;
16✔
118
  btf_type_id values = 0;
16✔
119

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

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

124
  for (const auto &member : map_struct.members) {
154✔
125
    if (member.name == "type") {
61✔
126
      type = member.type;
32✔
127
    } else if (member.name == "max_entries") {
45✔
128
      max_entries = member.type;
32✔
129
    } else if (member.name == "key") {
29✔
130
      key = btf_types.dereference_pointer(member.type);
18✔
131
    } else if (member.name == "value") {
20✔
132
      value = btf_types.dereference_pointer(member.type);
6✔
133
    } else if (member.name == "key_size") {
17✔
134
      key_size = member.type;
10✔
135
    } else if (member.name == "value_size") {
12✔
136
      value_size = member.type;
10✔
137
    } else if (member.name == "values") {
7✔
138
      values = member.type;
14✔
139
    }
140
  }
141

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

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

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

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

158
  if (key) {
32✔
159
    size_t key_size_in_bytes = btf_types.get_size(key);
18✔
160
    if (key_size_in_bytes > UINT32_MAX) {
9✔
161
      throw std::runtime_error("key size too large");
162
    }
163
    map_definition.key_size = static_cast<uint32_t>(key_size_in_bytes);
18✔
164
  } else if (key_size) {
14✔
165
    map_definition.key_size = _value_from_BTF__uint(btf_types, key_size);
10✔
166
  }
167

168
  if (value) {
32✔
169
    size_t value_size_in_bytes = btf_types.get_size(value);
6✔
170
    if (value_size_in_bytes > UINT32_MAX) {
3✔
171
      throw std::runtime_error("value size too large");
172
    }
173
    map_definition.value_size = static_cast<uint32_t>(value_size_in_bytes);
6✔
174
  } else if (value_size) {
26✔
175
    map_definition.value_size = _value_from_BTF__uint(btf_types, value_size);
10✔
176
  }
177

178
  if (values) {
32✔
179
    // Values is an array of pointers to BTF map definitions.
180
    auto values_array = btf_types.get_kind_type<btf_kind_array>(values);
14✔
181
    auto ptr = btf_types.get_kind_type<btf_kind_ptr>(values_array.element_type);
14✔
182

183
    auto inner_map_type_id = _unwrap_type(btf_types, ptr.type);
14✔
184

185
    if (_is_map_type(btf_types, inner_map_type_id)) {
14✔
186
      // Value is a map.
187
      // Store the inner map type ID and set value size to 4 bytes (the size of
188
      // a map id).
189
      map_definition.inner_map_type_id = static_cast<int>(inner_map_type_id);
12✔
190
      map_definition.value_size = sizeof(uint32_t);
12✔
191
    } else if (btf_types.get_kind_index(inner_map_type_id) ==
2✔
192
               BTF_KIND_FUNCTION_PROTOTYPE) {
193
      // Value is a BPF program.
194
      // Set the value size to 4 bytes (the size of a program id).
195
      map_definition.value_size = sizeof(uint32_t);
2✔
196
    } else {
197
      throw std::runtime_error("invalid type for values");
×
198
    }
199
  }
200

201
  return map_definition;
48✔
202
}
16✔
203

204
std::vector<btf_map_definition>
205
parse_btf_map_section(const btf_type_data &btf_data) {
22✔
206
  std::multimap<btf_type_id, btf_map_definition> map_definitions;
11✔
207

208
  if (btf_data.get_id(".maps") != 0) {
33✔
209
    std::set<btf_type_id> inner_map_type_ids;
10✔
210

211
    // Get the .maps data section.
212
    auto maps_section =
213
        btf_data.get_kind_type<btf_kind_data_section>(btf_data.get_id(".maps"));
30✔
214

215
    // Helper function to add a map definition to the map definitions and add
216
    // the inner map type ID to the list of inner map type IDs if it is present.
217
    auto handle_map_type_id = [&](const std::string &name,
32✔
218
                                  btf_type_id map_type_id) {
219
      auto map_definition =
220
          _get_map_definition_from_btf(btf_data, name, map_type_id);
32✔
221
      map_definitions.insert({map_definition.type_id, map_definition});
32✔
222
      // Add the inner map type ID to the list of inner map type IDs if it is
223
      // present.
224
      if (map_definition.inner_map_type_id != 0) {
32✔
225
        inner_map_type_ids.insert(map_definition.inner_map_type_id);
12✔
226
      }
227
    };
42✔
228

229
    // Add all maps in the .maps data section.
230
    for (const auto &var : maps_section.members) {
48✔
231
      auto map_var = btf_data.get_kind_type<btf_kind_var>(var.type);
28✔
232
      handle_map_type_id(map_var.name, map_var.type);
28✔
233
    }
14✔
234

235
    // Recursively add all inner maps. Assume that there are at most two levels
236
    // of inner maps. This is the current limit imposed by the BPF verifier on
237
    // Linux.
238
    for (size_t inner_map_recursion_level = 0; inner_map_recursion_level < 2;
60✔
239
         inner_map_recursion_level++) {
240
      // Add all maps that are not in the .maps data section.
241
      for (const auto &map_type_id : inner_map_type_ids) {
60✔
242
        // Skip if the map is already present.
243
        if (map_definitions.find(map_type_id) != map_definitions.end()) {
20✔
244
          continue;
16✔
245
        }
246
        handle_map_type_id("", map_type_id);
6✔
247
      }
248
    }
249
  }
20✔
250

251
  // Add an array map for this data section.
252
  auto handle_data_section = [&](btf_type_id data_section_id) {
6✔
253
    auto data_section =
254
        btf_data.get_kind_type<btf_kind_data_section>(data_section_id);
6✔
255
    if (data_section.members.empty()) { // Skip empty data sections.
6✔
256
      return;
257
    }
258
    btf_map_definition map_definition = {};
6✔
259
    map_definition.name = data_section.name;
3✔
260
    map_definition.type_id = data_section_id;
6✔
261
    map_definition.key_size = sizeof(uint32_t);
6✔
262
    map_definition.value_size =
6✔
263
        data_section.members.back().offset + data_section.members.back().size;
6✔
264
    map_definition.max_entries = 1;
6✔
265
    map_definitions.insert({map_definition.type_id, map_definition});
9✔
266
  };
6✔
267

268
  // Create a map for .bss, if it exists.
269
  if (btf_data.get_id(".bss") != 0) {
33✔
270
    handle_data_section(btf_data.get_id(".bss"));
3✔
271
  }
272

273
  // Create a map for .data, if it exists.
274
  if (btf_data.get_id(".data") != 0) {
33✔
275
    handle_data_section(btf_data.get_id(".data"));
3✔
276
  }
277

278
  // Create a map for .rodata, if it exists.
279
  if (btf_data.get_id(".rodata") != 0) {
33✔
280
    handle_data_section(btf_data.get_id(".rodata"));
3✔
281
  }
282

283
  std::vector<btf_map_definition> map_definitions_vector;
11✔
284
  for (const auto &map_definition : map_definitions) {
60✔
285
    map_definitions_vector.push_back(map_definition.second);
38✔
286
  }
287
  return map_definitions_vector;
33✔
288
}
11✔
289

290
btf_type_id btf_uint_from_value(btf_type_data &btf_data, uint32_t value) {
40✔
291
  btf_type_id int_id = btf_data.get_id("int");
40✔
292
  if (int_id == 0) {
40✔
293
    int_id = btf_data.append(btf_kind_int{
16✔
294
        .name = "int", .size_in_bytes = 4, .field_width_in_bits = 32});
295
  }
296

297
  btf_type_id array_size_id = btf_data.get_id("__ARRAY_SIZE_TYPE__");
40✔
298
  if (array_size_id == 0) {
40✔
299
    array_size_id = btf_data.append(btf_kind_int{
16✔
300
        .name = "__ARRAY_SIZE_TYPE__",
301
        .size_in_bytes = 4,
302
        .field_width_in_bits = 32,
303
    });
304
  }
305

306
  btf_type_id array = btf_data.append(btf_kind_array{
60✔
307
      .element_type = int_id,
308
      .index_type = array_size_id,
309
      .count_of_elements = value,
310
  });
311

312
  return btf_data.append(btf_kind_ptr{.type = array});
60✔
313
}
314

315
btf_type_id build_btf_map(btf_type_data &btf_data,
10✔
316
                          const btf_map_definition &map_definition) {
317
  uint32_t offset_in_bits = 0;
5✔
318
  btf_kind_struct map{
10✔
319
      .members =
320
          {
321
              {
322
                  .name = "type",
323
                  .type =
324
                      btf_uint_from_value(btf_data, map_definition.map_type),
10✔
325
              },
326
              {
327
                  .name = "max_entries",
328
                  .type =
329
                      btf_uint_from_value(btf_data, map_definition.max_entries),
10✔
330
              },
331
          },
332
  };
50✔
333
  if (map_definition.key_size) {
10✔
334
    map.members.push_back({
25✔
335
        .name = "key_size",
336
        .type = btf_uint_from_value(btf_data, map_definition.key_size),
10✔
337
    });
338
  }
339
  if (map_definition.value_size) {
10✔
340
    map.members.push_back({
25✔
341
        .name = "value_size",
342
        .type = btf_uint_from_value(btf_data, map_definition.value_size),
10✔
343
    });
344
  }
345

346
  if (map_definition.inner_map_type_id != 0) {
10✔
347
    map.members.push_back(
5✔
348
        {.name = "values",
349
         .type = btf_data.append({btf_kind_array{
4✔
350
             .element_type = btf_data.append(
3✔
351
                 btf_kind_ptr{.type = map_definition.inner_map_type_id}),
3✔
352
             .index_type = btf_data.get_id("__ARRAY_SIZE_TYPE__"),
4✔
353
         }})});
354
  }
355

356
  for (auto &member : map.members) {
52✔
357
    member.offset_from_start_in_bits = offset_in_bits;
42✔
358
    offset_in_bits += static_cast<uint32_t>(btf_data.get_size(member.type) * 8);
42✔
359
  }
360

361
  map.size_in_bytes = offset_in_bits / 8;
10✔
362

363
  return btf_data.append(btf_kind_var{
25✔
364
      .name = map_definition.name,
5✔
365
      .type = btf_data.append(map),
20✔
366
      .linkage = BTF_LINKAGE_STATIC,
367
  });
15✔
368
}
5✔
369

370
void build_btf_map_section(
8✔
371
    const std::vector<btf_map_definition> &map_definitions,
372
    btf_type_data &btf_data) {
373
  btf_kind_data_section maps_section{.name = ".maps"};
8✔
374
  std::map<btf_type_id, btf_type_id> old_id_to_new_id;
4✔
375

376
  for (auto &map_definition : map_definitions) {
18✔
377
    btf_type_id var_id = build_btf_map(btf_data, map_definition);
10✔
378
    btf_type_id map_id = btf_data.get_kind_type<btf_kind_var>(var_id).type;
10✔
379
    maps_section.members.push_back(btf_kind_data_member{
5✔
380
        .type = var_id,
381
        .size = static_cast<uint32_t>(btf_data.get_size(map_id)),
10✔
382
    });
383
    old_id_to_new_id[map_definition.type_id] = map_id;
10✔
384
  }
385

386
  // Update the map inner_map_type_id in the BTF types.
387
  for (auto &map_definition : map_definitions) {
18✔
388
    if (map_definition.inner_map_type_id == 0) {
10✔
389
      continue;
8✔
390
    }
391
    auto new_id = old_id_to_new_id[map_definition.type_id];
2✔
392
    auto ptr_id = btf_data
393
                      .get_kind_type<btf_kind_array>(
2✔
394
                          btf_data.get_kind_type<btf_kind_struct>(new_id)
2✔
395
                              .members.back()
1✔
396
                              .type)
397
                      .element_type;
2✔
398

399
    auto ptr = btf_data.get_kind_type<btf_kind_ptr>(ptr_id);
2✔
400
    ptr.type = old_id_to_new_id[ptr.type];
2✔
401

402
    btf_data.replace(ptr_id, ptr);
3✔
403
  }
404

405
  btf_data.append(maps_section);
12✔
406
  return;
12✔
407
}
8✔
408

409
} // namespace libbtf
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