1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2019 Facebook */ 3 4 #ifndef _GNU_SOURCE 5 #define _GNU_SOURCE 6 #endif 7 #include <ctype.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <linux/err.h> 11 #include <stdbool.h> 12 #include <stdio.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <bpf.h> 16 #include <libbpf.h> 17 #include <sys/types.h> 18 #include <sys/stat.h> 19 #include <unistd.h> 20 21 #include "btf.h" 22 #include "libbpf_internal.h" 23 #include "json_writer.h" 24 #include "main.h" 25 26 27 #define MAX_OBJ_NAME_LEN 64 28 29 static void sanitize_identifier(char *name) 30 { 31 int i; 32 33 for (i = 0; name[i]; i++) 34 if (!isalnum(name[i]) && name[i] != '_') 35 name[i] = '_'; 36 } 37 38 static bool str_has_suffix(const char *str, const char *suffix) 39 { 40 size_t i, n1 = strlen(str), n2 = strlen(suffix); 41 42 if (n1 < n2) 43 return false; 44 45 for (i = 0; i < n2; i++) { 46 if (str[n1 - i - 1] != suffix[n2 - i - 1]) 47 return false; 48 } 49 50 return true; 51 } 52 53 static void get_obj_name(char *name, const char *file) 54 { 55 /* Using basename() GNU version which doesn't modify arg. */ 56 strncpy(name, basename(file), MAX_OBJ_NAME_LEN - 1); 57 name[MAX_OBJ_NAME_LEN - 1] = '\0'; 58 if (str_has_suffix(name, ".o")) 59 name[strlen(name) - 2] = '\0'; 60 sanitize_identifier(name); 61 } 62 63 static void get_header_guard(char *guard, const char *obj_name) 64 { 65 int i; 66 67 sprintf(guard, "__%s_SKEL_H__", obj_name); 68 for (i = 0; guard[i]; i++) 69 guard[i] = toupper(guard[i]); 70 } 71 72 static const char *get_map_ident(const struct bpf_map *map) 73 { 74 const char *name = bpf_map__name(map); 75 76 if (!bpf_map__is_internal(map)) 77 return name; 78 79 if (str_has_suffix(name, ".data")) 80 return "data"; 81 else if (str_has_suffix(name, ".rodata")) 82 return "rodata"; 83 else if (str_has_suffix(name, ".bss")) 84 return "bss"; 85 else 86 return NULL; 87 } 88 89 static void codegen_btf_dump_printf(void *ct, const char *fmt, va_list args) 90 { 91 vprintf(fmt, args); 92 } 93 94 static int codegen_datasec_def(struct bpf_object *obj, 95 struct btf *btf, 96 struct btf_dump *d, 97 const struct btf_type *sec, 98 const char *obj_name) 99 { 100 const char *sec_name = btf__name_by_offset(btf, sec->name_off); 101 const struct btf_var_secinfo *sec_var = btf_var_secinfos(sec); 102 int i, err, off = 0, pad_cnt = 0, vlen = btf_vlen(sec); 103 const char *sec_ident; 104 char var_ident[256]; 105 106 if (strcmp(sec_name, ".data") == 0) 107 sec_ident = "data"; 108 else if (strcmp(sec_name, ".bss") == 0) 109 sec_ident = "bss"; 110 else if (strcmp(sec_name, ".rodata") == 0) 111 sec_ident = "rodata"; 112 else 113 return 0; 114 115 printf(" struct %s__%s {\n", obj_name, sec_ident); 116 for (i = 0; i < vlen; i++, sec_var++) { 117 const struct btf_type *var = btf__type_by_id(btf, sec_var->type); 118 const char *var_name = btf__name_by_offset(btf, var->name_off); 119 DECLARE_LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts, 120 .field_name = var_ident, 121 .indent_level = 2, 122 ); 123 int need_off = sec_var->offset, align_off, align; 124 __u32 var_type_id = var->type; 125 const struct btf_type *t; 126 127 t = btf__type_by_id(btf, var_type_id); 128 while (btf_is_mod(t)) { 129 var_type_id = t->type; 130 t = btf__type_by_id(btf, var_type_id); 131 } 132 133 if (off > need_off) { 134 p_err("Something is wrong for %s's variable #%d: need offset %d, already at %d.\n", 135 sec_name, i, need_off, off); 136 return -EINVAL; 137 } 138 139 align = btf__align_of(btf, var->type); 140 if (align <= 0) { 141 p_err("Failed to determine alignment of variable '%s': %d", 142 var_name, align); 143 return -EINVAL; 144 } 145 146 align_off = (off + align - 1) / align * align; 147 if (align_off != need_off) { 148 printf("\t\tchar __pad%d[%d];\n", 149 pad_cnt, need_off - off); 150 pad_cnt++; 151 } 152 153 /* sanitize variable name, e.g., for static vars inside 154 * a function, it's name is '<function name>.<variable name>', 155 * which we'll turn into a '<function name>_<variable name>' 156 */ 157 var_ident[0] = '\0'; 158 strncat(var_ident, var_name, sizeof(var_ident) - 1); 159 sanitize_identifier(var_ident); 160 161 printf("\t\t"); 162 err = btf_dump__emit_type_decl(d, var_type_id, &opts); 163 if (err) 164 return err; 165 printf(";\n"); 166 167 off = sec_var->offset + sec_var->size; 168 } 169 printf(" } *%s;\n", sec_ident); 170 return 0; 171 } 172 173 static int codegen_datasecs(struct bpf_object *obj, const char *obj_name) 174 { 175 struct btf *btf = bpf_object__btf(obj); 176 int n = btf__get_nr_types(btf); 177 struct btf_dump *d; 178 int i, err = 0; 179 180 d = btf_dump__new(btf, NULL, NULL, codegen_btf_dump_printf); 181 if (IS_ERR(d)) 182 return PTR_ERR(d); 183 184 for (i = 1; i <= n; i++) { 185 const struct btf_type *t = btf__type_by_id(btf, i); 186 187 if (!btf_is_datasec(t)) 188 continue; 189 190 err = codegen_datasec_def(obj, btf, d, t, obj_name); 191 if (err) 192 goto out; 193 } 194 out: 195 btf_dump__free(d); 196 return err; 197 } 198 199 static int codegen(const char *template, ...) 200 { 201 const char *src, *end; 202 int skip_tabs = 0, n; 203 char *s, *dst; 204 va_list args; 205 char c; 206 207 n = strlen(template); 208 s = malloc(n + 1); 209 if (!s) 210 return -ENOMEM; 211 src = template; 212 dst = s; 213 214 /* find out "baseline" indentation to skip */ 215 while ((c = *src++)) { 216 if (c == '\t') { 217 skip_tabs++; 218 } else if (c == '\n') { 219 break; 220 } else { 221 p_err("unrecognized character at pos %td in template '%s'", 222 src - template - 1, template); 223 return -EINVAL; 224 } 225 } 226 227 while (*src) { 228 /* skip baseline indentation tabs */ 229 for (n = skip_tabs; n > 0; n--, src++) { 230 if (*src != '\t') { 231 p_err("not enough tabs at pos %td in template '%s'", 232 src - template - 1, template); 233 return -EINVAL; 234 } 235 } 236 /* trim trailing whitespace */ 237 end = strchrnul(src, '\n'); 238 for (n = end - src; n > 0 && isspace(src[n - 1]); n--) 239 ; 240 memcpy(dst, src, n); 241 dst += n; 242 if (*end) 243 *dst++ = '\n'; 244 src = *end ? end + 1 : end; 245 } 246 *dst++ = '\0'; 247 248 /* print out using adjusted template */ 249 va_start(args, template); 250 n = vprintf(s, args); 251 va_end(args); 252 253 free(s); 254 return n; 255 } 256 257 static int do_skeleton(int argc, char **argv) 258 { 259 char header_guard[MAX_OBJ_NAME_LEN + sizeof("__SKEL_H__")]; 260 size_t i, map_cnt = 0, prog_cnt = 0; 261 char obj_name[MAX_OBJ_NAME_LEN]; 262 const char *file, *ident; 263 struct bpf_program *prog; 264 struct bpf_object *obj; 265 struct bpf_map *map; 266 struct btf *btf; 267 int err = -1; 268 269 if (!REQ_ARGS(1)) { 270 usage(); 271 return -1; 272 } 273 file = GET_ARG(); 274 275 if (argc) { 276 p_err("extra unknown arguments"); 277 return -1; 278 } 279 280 obj = bpf_object__open_file(file, NULL); 281 if (IS_ERR(obj)) { 282 p_err("failed to open BPF object file: %ld", PTR_ERR(obj)); 283 return -1; 284 } 285 286 get_obj_name(obj_name, file); 287 get_header_guard(header_guard, obj_name); 288 289 bpf_object__for_each_map(map, obj) { 290 ident = get_map_ident(map); 291 if (!ident) { 292 p_err("ignoring unrecognized internal map '%s'...", 293 bpf_map__name(map)); 294 continue; 295 } 296 map_cnt++; 297 } 298 bpf_object__for_each_program(prog, obj) { 299 prog_cnt++; 300 } 301 302 codegen("\ 303 \n\ 304 /* THIS FILE IS AUTOGENERATED! */ \n\ 305 #ifndef %2$s \n\ 306 #define %2$s \n\ 307 \n\ 308 #include <stdlib.h> \n\ 309 #include <libbpf.h> \n\ 310 \n\ 311 struct %1$s { \n\ 312 struct bpf_object_skeleton *skeleton; \n\ 313 struct bpf_object *obj; \n\ 314 ", 315 obj_name, header_guard 316 ); 317 318 if (map_cnt) { 319 printf("\tstruct {\n"); 320 bpf_object__for_each_map(map, obj) { 321 ident = get_map_ident(map); 322 if (!ident) 323 continue; 324 printf("\t\tstruct bpf_map *%s;\n", ident); 325 } 326 printf("\t} maps;\n"); 327 } 328 329 if (prog_cnt) { 330 printf("\tstruct {\n"); 331 bpf_object__for_each_program(prog, obj) { 332 printf("\t\tstruct bpf_program *%s;\n", 333 bpf_program__name(prog)); 334 } 335 printf("\t} progs;\n"); 336 printf("\tstruct {\n"); 337 bpf_object__for_each_program(prog, obj) { 338 printf("\t\tstruct bpf_link *%s;\n", 339 bpf_program__name(prog)); 340 } 341 printf("\t} links;\n"); 342 } 343 344 btf = bpf_object__btf(obj); 345 if (btf) { 346 err = codegen_datasecs(obj, obj_name); 347 if (err) 348 goto out; 349 } 350 351 codegen("\ 352 \n\ 353 }; \n\ 354 \n\ 355 static inline struct bpf_object_skeleton * \n\ 356 %1$s__create_skeleton(struct %1$s *obj, struct bpf_embed_data *embed)\n\ 357 { \n\ 358 struct bpf_object_skeleton *s; \n\ 359 \n\ 360 s = calloc(1, sizeof(*s)); \n\ 361 if (!s) \n\ 362 return NULL; \n\ 363 \n\ 364 s->sz = sizeof(*s); \n\ 365 s->name = \"%1$s\"; \n\ 366 s->data = embed->data; \n\ 367 s->data_sz = embed->size; \n\ 368 s->obj = &obj->obj; \n\ 369 ", 370 obj_name 371 ); 372 if (map_cnt) { 373 codegen("\ 374 \n\ 375 \n\ 376 /* maps */ \n\ 377 s->map_cnt = %zu; \n\ 378 s->map_skel_sz = sizeof(*s->maps); \n\ 379 s->maps = calloc(s->map_cnt, s->map_skel_sz);\n\ 380 if (!s->maps) \n\ 381 goto err; \n\ 382 ", 383 map_cnt 384 ); 385 i = 0; 386 bpf_object__for_each_map(map, obj) { 387 const char *ident = get_map_ident(map); 388 389 if (!ident) 390 continue; 391 392 codegen("\ 393 \n\ 394 \n\ 395 s->maps[%zu].name = \"%s\"; \n\ 396 s->maps[%zu].map = &obj->maps.%s; \n\ 397 ", 398 i, bpf_map__name(map), i, ident); 399 /* memory-mapped internal maps */ 400 if (bpf_map__is_internal(map) && 401 (bpf_map__def(map)->map_flags & BPF_F_MMAPABLE)) { 402 printf("\ts->maps[%zu].mmaped = (void **)&obj->%s;\n", 403 i, ident); 404 } 405 i++; 406 } 407 } 408 if (prog_cnt) { 409 codegen("\ 410 \n\ 411 \n\ 412 /* programs */ \n\ 413 s->prog_cnt = %zu; \n\ 414 s->prog_skel_sz = sizeof(*s->progs); \n\ 415 s->progs = calloc(s->prog_cnt, s->prog_skel_sz);\n\ 416 if (!s->progs) \n\ 417 goto err; \n\ 418 ", 419 prog_cnt 420 ); 421 i = 0; 422 bpf_object__for_each_program(prog, obj) { 423 codegen("\ 424 \n\ 425 \n\ 426 s->progs[%1$zu].name = \"%2$s\"; \n\ 427 s->progs[%1$zu].prog = &obj->progs.%2$s;\n\ 428 s->progs[%1$zu].link = &obj->links.%2$s;\n\ 429 ", 430 i, bpf_program__name(prog)); 431 i++; 432 } 433 } 434 codegen("\ 435 \n\ 436 \n\ 437 return s; \n\ 438 err: \n\ 439 bpf_object__destroy_skeleton(s); \n\ 440 return NULL; \n\ 441 } \n\ 442 \n\ 443 static void \n\ 444 %1$s__destroy(struct %1$s *obj) \n\ 445 { \n\ 446 if (!obj) \n\ 447 return; \n\ 448 if (obj->skeleton) \n\ 449 bpf_object__destroy_skeleton(obj->skeleton);\n\ 450 free(obj); \n\ 451 } \n\ 452 \n\ 453 static inline struct %1$s * \n\ 454 %1$s__open_opts(struct bpf_embed_data *embed, const struct bpf_object_open_opts *opts)\n\ 455 { \n\ 456 struct %1$s *obj; \n\ 457 \n\ 458 obj = calloc(1, sizeof(*obj)); \n\ 459 if (!obj) \n\ 460 return NULL; \n\ 461 \n\ 462 obj->skeleton = %1$s__create_skeleton(obj, embed); \n\ 463 if (!obj->skeleton) \n\ 464 goto err; \n\ 465 \n\ 466 if (bpf_object__open_skeleton(obj->skeleton, opts)) \n\ 467 goto err; \n\ 468 \n\ 469 return obj; \n\ 470 err: \n\ 471 %1$s__destroy(obj); \n\ 472 return NULL; \n\ 473 } \n\ 474 \n\ 475 static inline struct %1$s * \n\ 476 %1$s__open(struct bpf_embed_data *embed) \n\ 477 { \n\ 478 return %1$s__open_opts(embed, NULL); \n\ 479 } \n\ 480 \n\ 481 static inline int \n\ 482 %1$s__load(struct %1$s *obj) \n\ 483 { \n\ 484 return bpf_object__load_skeleton(obj->skeleton); \n\ 485 } \n\ 486 \n\ 487 static inline struct %1$s * \n\ 488 %1$s__open_and_load(struct bpf_embed_data *embed) \n\ 489 { \n\ 490 struct %1$s *obj; \n\ 491 \n\ 492 obj = %1$s__open(embed); \n\ 493 if (!obj) \n\ 494 return NULL; \n\ 495 if (%1$s__load(obj)) { \n\ 496 %1$s__destroy(obj); \n\ 497 return NULL; \n\ 498 } \n\ 499 return obj; \n\ 500 } \n\ 501 \n\ 502 static inline int \n\ 503 %1$s__attach(struct %1$s *obj) \n\ 504 { \n\ 505 return bpf_object__attach_skeleton(obj->skeleton); \n\ 506 } \n\ 507 \n\ 508 static inline void \n\ 509 %1$s__detach(struct %1$s *obj) \n\ 510 { \n\ 511 return bpf_object__detach_skeleton(obj->skeleton); \n\ 512 } \n\ 513 \n\ 514 #endif /* %2$s */ \n\ 515 ", 516 obj_name, header_guard 517 ); 518 err = 0; 519 out: 520 bpf_object__close(obj); 521 return err; 522 } 523 524 static int do_help(int argc, char **argv) 525 { 526 if (json_output) { 527 jsonw_null(json_wtr); 528 return 0; 529 } 530 531 fprintf(stderr, 532 "Usage: %1$s gen skeleton FILE\n" 533 " %1$s gen help\n" 534 "\n" 535 " " HELP_SPEC_OPTIONS "\n" 536 "", 537 bin_name); 538 539 return 0; 540 } 541 542 static const struct cmd cmds[] = { 543 { "skeleton", do_skeleton }, 544 { "help", do_help }, 545 { 0 } 546 }; 547 548 int do_gen(int argc, char **argv) 549 { 550 return cmd_select(cmds, argc, argv, do_help); 551 } 552