|
16 | 16 |
|
17 | 17 | #include <linux/hashtable.h> |
18 | 18 | #include <linux/kernel.h> |
| 19 | +#include <linux/static_call_types.h> |
19 | 20 |
|
20 | 21 | #define FAKE_JUMP_OFFSET -1 |
21 | 22 |
|
@@ -433,6 +434,103 @@ static int add_dead_ends(struct objtool_file *file) |
433 | 434 | return 0; |
434 | 435 | } |
435 | 436 |
|
| 437 | +static int create_static_call_sections(struct objtool_file *file) |
| 438 | +{ |
| 439 | + struct section *sec, *reloc_sec; |
| 440 | + struct reloc *reloc; |
| 441 | + struct static_call_site *site; |
| 442 | + struct instruction *insn; |
| 443 | + struct symbol *key_sym; |
| 444 | + char *key_name, *tmp; |
| 445 | + int idx; |
| 446 | + |
| 447 | + sec = find_section_by_name(file->elf, ".static_call_sites"); |
| 448 | + if (sec) { |
| 449 | + INIT_LIST_HEAD(&file->static_call_list); |
| 450 | + WARN("file already has .static_call_sites section, skipping"); |
| 451 | + return 0; |
| 452 | + } |
| 453 | + |
| 454 | + if (list_empty(&file->static_call_list)) |
| 455 | + return 0; |
| 456 | + |
| 457 | + idx = 0; |
| 458 | + list_for_each_entry(insn, &file->static_call_list, static_call_node) |
| 459 | + idx++; |
| 460 | + |
| 461 | + sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE, |
| 462 | + sizeof(struct static_call_site), idx); |
| 463 | + if (!sec) |
| 464 | + return -1; |
| 465 | + |
| 466 | + reloc_sec = elf_create_reloc_section(file->elf, sec, SHT_RELA); |
| 467 | + if (!reloc_sec) |
| 468 | + return -1; |
| 469 | + |
| 470 | + idx = 0; |
| 471 | + list_for_each_entry(insn, &file->static_call_list, static_call_node) { |
| 472 | + |
| 473 | + site = (struct static_call_site *)sec->data->d_buf + idx; |
| 474 | + memset(site, 0, sizeof(struct static_call_site)); |
| 475 | + |
| 476 | + /* populate reloc for 'addr' */ |
| 477 | + reloc = malloc(sizeof(*reloc)); |
| 478 | + if (!reloc) { |
| 479 | + perror("malloc"); |
| 480 | + return -1; |
| 481 | + } |
| 482 | + memset(reloc, 0, sizeof(*reloc)); |
| 483 | + reloc->sym = insn->sec->sym; |
| 484 | + reloc->addend = insn->offset; |
| 485 | + reloc->type = R_X86_64_PC32; |
| 486 | + reloc->offset = idx * sizeof(struct static_call_site); |
| 487 | + reloc->sec = reloc_sec; |
| 488 | + elf_add_reloc(file->elf, reloc); |
| 489 | + |
| 490 | + /* find key symbol */ |
| 491 | + key_name = strdup(insn->call_dest->name); |
| 492 | + if (!key_name) { |
| 493 | + perror("strdup"); |
| 494 | + return -1; |
| 495 | + } |
| 496 | + if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR, |
| 497 | + STATIC_CALL_TRAMP_PREFIX_LEN)) { |
| 498 | + WARN("static_call: trampoline name malformed: %s", key_name); |
| 499 | + return -1; |
| 500 | + } |
| 501 | + tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN; |
| 502 | + memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN); |
| 503 | + |
| 504 | + key_sym = find_symbol_by_name(file->elf, tmp); |
| 505 | + if (!key_sym) { |
| 506 | + WARN("static_call: can't find static_call_key symbol: %s", tmp); |
| 507 | + return -1; |
| 508 | + } |
| 509 | + free(key_name); |
| 510 | + |
| 511 | + /* populate reloc for 'key' */ |
| 512 | + reloc = malloc(sizeof(*reloc)); |
| 513 | + if (!reloc) { |
| 514 | + perror("malloc"); |
| 515 | + return -1; |
| 516 | + } |
| 517 | + memset(reloc, 0, sizeof(*reloc)); |
| 518 | + reloc->sym = key_sym; |
| 519 | + reloc->addend = 0; |
| 520 | + reloc->type = R_X86_64_PC32; |
| 521 | + reloc->offset = idx * sizeof(struct static_call_site) + 4; |
| 522 | + reloc->sec = reloc_sec; |
| 523 | + elf_add_reloc(file->elf, reloc); |
| 524 | + |
| 525 | + idx++; |
| 526 | + } |
| 527 | + |
| 528 | + if (elf_rebuild_reloc_section(file->elf, reloc_sec)) |
| 529 | + return -1; |
| 530 | + |
| 531 | + return 0; |
| 532 | +} |
| 533 | + |
436 | 534 | /* |
437 | 535 | * Warnings shouldn't be reported for ignored functions. |
438 | 536 | */ |
@@ -1522,6 +1620,23 @@ static int read_intra_function_calls(struct objtool_file *file) |
1522 | 1620 | return 0; |
1523 | 1621 | } |
1524 | 1622 |
|
| 1623 | +static int read_static_call_tramps(struct objtool_file *file) |
| 1624 | +{ |
| 1625 | + struct section *sec; |
| 1626 | + struct symbol *func; |
| 1627 | + |
| 1628 | + for_each_sec(file, sec) { |
| 1629 | + list_for_each_entry(func, &sec->symbol_list, list) { |
| 1630 | + if (func->bind == STB_GLOBAL && |
| 1631 | + !strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR, |
| 1632 | + strlen(STATIC_CALL_TRAMP_PREFIX_STR))) |
| 1633 | + func->static_call_tramp = true; |
| 1634 | + } |
| 1635 | + } |
| 1636 | + |
| 1637 | + return 0; |
| 1638 | +} |
| 1639 | + |
1525 | 1640 | static void mark_rodata(struct objtool_file *file) |
1526 | 1641 | { |
1527 | 1642 | struct section *sec; |
@@ -1601,6 +1716,10 @@ static int decode_sections(struct objtool_file *file) |
1601 | 1716 | if (ret) |
1602 | 1717 | return ret; |
1603 | 1718 |
|
| 1719 | + ret = read_static_call_tramps(file); |
| 1720 | + if (ret) |
| 1721 | + return ret; |
| 1722 | + |
1604 | 1723 | return 0; |
1605 | 1724 | } |
1606 | 1725 |
|
@@ -2432,6 +2551,11 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, |
2432 | 2551 | if (dead_end_function(file, insn->call_dest)) |
2433 | 2552 | return 0; |
2434 | 2553 |
|
| 2554 | + if (insn->type == INSN_CALL && insn->call_dest->static_call_tramp) { |
| 2555 | + list_add_tail(&insn->static_call_node, |
| 2556 | + &file->static_call_list); |
| 2557 | + } |
| 2558 | + |
2435 | 2559 | break; |
2436 | 2560 |
|
2437 | 2561 | case INSN_JUMP_CONDITIONAL: |
@@ -2791,6 +2915,7 @@ int check(const char *_objname, bool orc) |
2791 | 2915 |
|
2792 | 2916 | INIT_LIST_HEAD(&file.insn_list); |
2793 | 2917 | hash_init(file.insn_hash); |
| 2918 | + INIT_LIST_HEAD(&file.static_call_list); |
2794 | 2919 | file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment"); |
2795 | 2920 | file.ignore_unreachables = no_unreachable; |
2796 | 2921 | file.hints = false; |
@@ -2838,6 +2963,11 @@ int check(const char *_objname, bool orc) |
2838 | 2963 | warnings += ret; |
2839 | 2964 | } |
2840 | 2965 |
|
| 2966 | + ret = create_static_call_sections(&file); |
| 2967 | + if (ret < 0) |
| 2968 | + goto out; |
| 2969 | + warnings += ret; |
| 2970 | + |
2841 | 2971 | if (orc) { |
2842 | 2972 | ret = create_orc(&file); |
2843 | 2973 | if (ret < 0) |
|
0 commit comments