diff options
| author | Sam Ravnborg <sam@mars.ravnborg.org> | 2006-02-17 16:42:02 -0500 |
|---|---|---|
| committer | Sam Ravnborg <sam@mars.ravnborg.org> | 2006-02-19 03:51:20 -0500 |
| commit | b39927cf4cc5a9123d2b157ffd396884cb8156eb (patch) | |
| tree | 8e372f4b8fa678de7239a125ff4653f136a9dc66 /scripts | |
| parent | a67dc21a38055ec2d8d85b2f64d98091748569b3 (diff) | |
kbuild: check for section mismatch during modpost stage
Section mismatch is identified as references to .init*
sections from non .init sections. And likewise references
to .exit.* sections outside .exit sections.
.init.* sections are discarded after a module is initialized
and references to .init.* sections are oops candidates.
.exit.* sections are discarded when a module is built-in and
thus references to .exit are also oops candidates.
The checks were possible to do using 'make buildcheck' which
called the two perl scripts: reference_discarded.pl and
reference_init.pl. This patch just moves the same functionality
inside modpost and the scripts are then obsoleted.
They will though be kept for a while so users can do double
checks - but note that some .o files are skipped by the perl scripts
so result is not 1:1.
All credit for the concept goes to Keith Owens who implemented
the original perl scrips - this patch just moves it to modpost.
Compared to the perl script the implmentation in modpost will be run
for each kernel build - thus catching the error much sooner, but
the downside is that the individual .o file are not always identified.
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/mod/modpost.c | 258 | ||||
| -rw-r--r-- | scripts/mod/modpost.h | 10 |
2 files changed, 268 insertions, 0 deletions
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d901095ea8..a7360c379c 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c | |||
| @@ -451,6 +451,262 @@ static char *get_modinfo(void *modinfo, unsigned long modinfo_len, | |||
| 451 | return NULL; | 451 | return NULL; |
| 452 | } | 452 | } |
| 453 | 453 | ||
| 454 | /* | ||
| 455 | * Find symbols before or equal addr and after addr - in the section sec | ||
| 456 | **/ | ||
| 457 | static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, | ||
| 458 | const char *sec, | ||
| 459 | Elf_Sym **before, Elf_Sym **after) | ||
| 460 | { | ||
| 461 | Elf_Sym *sym; | ||
| 462 | Elf_Ehdr *hdr = elf->hdr; | ||
| 463 | Elf_Addr beforediff = ~0; | ||
| 464 | Elf_Addr afterdiff = ~0; | ||
| 465 | const char *secstrings = (void *)hdr + | ||
| 466 | elf->sechdrs[hdr->e_shstrndx].sh_offset; | ||
| 467 | |||
| 468 | *before = NULL; | ||
| 469 | *after = NULL; | ||
| 470 | |||
| 471 | for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { | ||
| 472 | const char *symsec; | ||
| 473 | |||
| 474 | if (sym->st_shndx >= SHN_LORESERVE) | ||
| 475 | continue; | ||
| 476 | symsec = secstrings + elf->sechdrs[sym->st_shndx].sh_name; | ||
| 477 | if (strcmp(symsec, sec) != 0) | ||
| 478 | continue; | ||
| 479 | if (sym->st_value <= addr) { | ||
| 480 | if ((addr - sym->st_value) < beforediff) { | ||
| 481 | beforediff = addr - sym->st_value; | ||
| 482 | *before = sym; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | else | ||
| 486 | { | ||
| 487 | if ((sym->st_value - addr) < afterdiff) { | ||
| 488 | afterdiff = sym->st_value - addr; | ||
| 489 | *after = sym; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | /** | ||
| 496 | * Print a warning about a section mismatch. | ||
| 497 | * Try to find symbols near it so user can find it. | ||
| 498 | **/ | ||
| 499 | static void warn_sec_mismatch(const char *modname, const char *fromsec, | ||
| 500 | struct elf_info *elf, Elf_Sym *sym, Elf_Rela r) | ||
| 501 | { | ||
| 502 | Elf_Sym *before; | ||
| 503 | Elf_Sym *after; | ||
| 504 | Elf_Ehdr *hdr = elf->hdr; | ||
| 505 | Elf_Shdr *sechdrs = elf->sechdrs; | ||
| 506 | const char *secstrings = (void *)hdr + | ||
| 507 | sechdrs[hdr->e_shstrndx].sh_offset; | ||
| 508 | const char *secname = secstrings + sechdrs[sym->st_shndx].sh_name; | ||
| 509 | |||
| 510 | find_symbols_between(elf, r.r_offset, fromsec, &before, &after); | ||
| 511 | |||
| 512 | if (before && after) { | ||
| 513 | warn("%s - Section mismatch: reference to %s from %s " | ||
| 514 | "between '%s' (at offset 0x%lx) and '%s'\n", | ||
| 515 | modname, secname, fromsec, | ||
| 516 | elf->strtab + before->st_name, | ||
| 517 | (long)(r.r_offset - before->st_value), | ||
| 518 | elf->strtab + after->st_name); | ||
| 519 | } else if (before) { | ||
| 520 | warn("%s - Section mismatch: reference to %s from %s " | ||
| 521 | "after '%s' (at offset 0x%lx)\n", | ||
| 522 | modname, secname, fromsec, | ||
| 523 | elf->strtab + before->st_name, | ||
| 524 | (long)(r.r_offset - before->st_value)); | ||
| 525 | } else if (after) { | ||
| 526 | warn("%s - Section mismatch: reference to %s from %s " | ||
| 527 | "before '%s' (at offset -0x%lx)\n", | ||
| 528 | modname, secname, fromsec, | ||
| 529 | elf->strtab + before->st_name, | ||
| 530 | (long)(before->st_value - r.r_offset)); | ||
| 531 | } else { | ||
| 532 | warn("%s - Section mismatch: reference to %s from %s " | ||
| 533 | "(offset 0x%lx)\n", | ||
| 534 | modname, secname, fromsec, (long)r.r_offset); | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | /** | ||
| 539 | * A module includes a number of sections that are discarded | ||
| 540 | * either when loaded or when used as built-in. | ||
| 541 | * For loaded modules all functions marked __init and all data | ||
| 542 | * marked __initdata will be discarded when the module has been intialized. | ||
| 543 | * Likewise for modules used built-in the sections marked __exit | ||
| 544 | * are discarded because __exit marked function are supposed to be called | ||
| 545 | * only when a moduel is unloaded which never happes for built-in modules. | ||
| 546 | * The check_sec_ref() function traverses all relocation records | ||
| 547 | * to find all references to a section that reference a section that will | ||
| 548 | * be discarded and warns about it. | ||
| 549 | **/ | ||
| 550 | static void check_sec_ref(struct module *mod, const char *modname, | ||
| 551 | struct elf_info *elf, | ||
| 552 | int section(const char*), | ||
| 553 | int section_ref_ok(const char *)) | ||
| 554 | { | ||
| 555 | int i; | ||
| 556 | Elf_Sym *sym; | ||
| 557 | Elf_Ehdr *hdr = elf->hdr; | ||
| 558 | Elf_Shdr *sechdrs = elf->sechdrs; | ||
| 559 | const char *secstrings = (void *)hdr + | ||
| 560 | sechdrs[hdr->e_shstrndx].sh_offset; | ||
| 561 | |||
| 562 | /* Walk through all sections */ | ||
| 563 | for (i = 0; i < hdr->e_shnum; i++) { | ||
| 564 | const char *name = secstrings + sechdrs[i].sh_name + | ||
| 565 | strlen(".rela"); | ||
| 566 | /* We want to process only relocation sections and not .init */ | ||
| 567 | if (section_ref_ok(name) || (sechdrs[i].sh_type != SHT_RELA)) | ||
| 568 | continue; | ||
| 569 | Elf_Rela *rela; | ||
| 570 | Elf_Rela *start = (void *)hdr + sechdrs[i].sh_offset; | ||
| 571 | Elf_Rela *stop = (void*)start + sechdrs[i].sh_size; | ||
| 572 | |||
| 573 | for (rela = start; rela < stop; rela++) { | ||
| 574 | Elf_Rela r; | ||
| 575 | const char *secname; | ||
| 576 | r.r_offset = TO_NATIVE(rela->r_offset); | ||
| 577 | r.r_info = TO_NATIVE(rela->r_info); | ||
| 578 | sym = elf->symtab_start + ELF_R_SYM(r.r_info); | ||
| 579 | secname = secstrings + sechdrs[sym->st_shndx].sh_name; | ||
| 580 | /* Skip special sections */ | ||
| 581 | if (sym->st_shndx >= SHN_LORESERVE) | ||
| 582 | continue; | ||
| 583 | |||
| 584 | if (section(secname)) | ||
| 585 | warn_sec_mismatch(modname, name, elf, sym, r); | ||
| 586 | } | ||
| 587 | } | ||
| 588 | } | ||
| 589 | |||
| 590 | /** | ||
| 591 | * Functions used only during module init is marked __init and is stored in | ||
| 592 | * a .init.text section. Likewise data is marked __initdata and stored in | ||
| 593 | * a .init.data section. | ||
| 594 | * If this section is one of these sections return 1 | ||
| 595 | * See include/linux/init.h for the details | ||
| 596 | **/ | ||
| 597 | static int init_section(const char *name) | ||
| 598 | { | ||
| 599 | if (strcmp(name, ".init") == 0) | ||
| 600 | return 1; | ||
| 601 | if (strncmp(name, ".init.", strlen(".init.")) == 0) | ||
| 602 | return 1; | ||
| 603 | return 0; | ||
| 604 | } | ||
| 605 | |||
| 606 | /** | ||
| 607 | * Identify sections from which references to a .init section is OK. | ||
| 608 | * | ||
| 609 | * Unfortunately references to read only data that referenced .init | ||
| 610 | * sections had to be excluded. Almost all of these are false | ||
| 611 | * positives, they are created by gcc. The downside of excluding rodata | ||
| 612 | * is that there really are some user references from rodata to | ||
| 613 | * init code, e.g. drivers/video/vgacon.c: | ||
| 614 | * | ||
| 615 | * const struct consw vga_con = { | ||
| 616 | * con_startup: vgacon_startup, | ||
| 617 | * | ||
| 618 | * where vgacon_startup is __init. If you want to wade through the false | ||
| 619 | * positives, take out the check for rodata. | ||
| 620 | **/ | ||
| 621 | static int init_section_ref_ok(const char *name) | ||
| 622 | { | ||
| 623 | const char **s; | ||
| 624 | /* Absolute section names */ | ||
| 625 | const char *namelist1[] = { | ||
| 626 | ".init", | ||
| 627 | ".stab", | ||
| 628 | ".rodata", | ||
| 629 | ".text.lock", | ||
| 630 | ".pci_fixup_header", | ||
| 631 | ".pci_fixup_final", | ||
| 632 | ".pdr", | ||
| 633 | "__param", | ||
| 634 | NULL | ||
| 635 | }; | ||
| 636 | /* Start of section names */ | ||
| 637 | const char *namelist2[] = { | ||
| 638 | ".init.", | ||
| 639 | ".altinstructions", | ||
| 640 | ".eh_frame", | ||
| 641 | ".debug", | ||
| 642 | NULL | ||
| 643 | }; | ||
| 644 | |||
| 645 | for (s = namelist1; *s; s++) | ||
| 646 | if (strcmp(*s, name) == 0) | ||
| 647 | return 1; | ||
| 648 | for (s = namelist2; *s; s++) | ||
| 649 | if (strncmp(*s, name, strlen(*s)) == 0) | ||
| 650 | return 1; | ||
| 651 | return 0; | ||
| 652 | } | ||
| 653 | |||
| 654 | /* | ||
| 655 | * Functions used only during module exit is marked __exit and is stored in | ||
| 656 | * a .exit.text section. Likewise data is marked __exitdata and stored in | ||
| 657 | * a .exit.data section. | ||
| 658 | * If this section is one of these sections return 1 | ||
| 659 | * See include/linux/init.h for the details | ||
| 660 | **/ | ||
| 661 | static int exit_section(const char *name) | ||
| 662 | { | ||
| 663 | if (strcmp(name, ".exit.text") == 0) | ||
| 664 | return 1; | ||
| 665 | if (strcmp(name, ".exit.data") == 0) | ||
| 666 | return 1; | ||
| 667 | return 0; | ||
| 668 | |||
| 669 | } | ||
| 670 | |||
| 671 | /* | ||
| 672 | * Identify sections from which references to a .exit section is OK. | ||
| 673 | * | ||
| 674 | * [OPD] Keith Ownes <kaos@sgi.com> commented: | ||
| 675 | * For our future {in}sanity, add a comment that this is the ppc .opd | ||
| 676 | * section, not the ia64 .opd section. | ||
| 677 | * ia64 .opd should not point to discarded sections. | ||
| 678 | **/ | ||
| 679 | static int exit_section_ref_ok(const char *name) | ||
| 680 | { | ||
| 681 | const char **s; | ||
| 682 | /* Absolute section names */ | ||
| 683 | const char *namelist1[] = { | ||
| 684 | ".exit.text", | ||
| 685 | ".exit.data", | ||
| 686 | ".init.text", | ||
| 687 | ".opd", /* See comment [OPD] */ | ||
| 688 | ".altinstructions", | ||
| 689 | ".pdr", | ||
| 690 | ".exitcall.exit", | ||
| 691 | ".eh_frame", | ||
| 692 | ".stab", | ||
| 693 | NULL | ||
| 694 | }; | ||
| 695 | /* Start of section names */ | ||
| 696 | const char *namelist2[] = { | ||
| 697 | ".debug", | ||
| 698 | NULL | ||
| 699 | }; | ||
| 700 | |||
| 701 | for (s = namelist1; *s; s++) | ||
| 702 | if (strcmp(*s, name) == 0) | ||
| 703 | return 1; | ||
| 704 | for (s = namelist2; *s; s++) | ||
| 705 | if (strncmp(*s, name, strlen(*s)) == 0) | ||
| 706 | return 1; | ||
| 707 | return 0; | ||
| 708 | } | ||
| 709 | |||
| 454 | static void read_symbols(char *modname) | 710 | static void read_symbols(char *modname) |
| 455 | { | 711 | { |
| 456 | const char *symname; | 712 | const char *symname; |
| @@ -476,6 +732,8 @@ static void read_symbols(char *modname) | |||
| 476 | handle_modversions(mod, &info, sym, symname); | 732 | handle_modversions(mod, &info, sym, symname); |
| 477 | handle_moddevtable(mod, &info, sym, symname); | 733 | handle_moddevtable(mod, &info, sym, symname); |
| 478 | } | 734 | } |
| 735 | check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok); | ||
| 736 | check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok); | ||
| 479 | 737 | ||
| 480 | version = get_modinfo(info.modinfo, info.modinfo_len, "version"); | 738 | version = get_modinfo(info.modinfo, info.modinfo_len, "version"); |
| 481 | if (version) | 739 | if (version) |
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index c0de7b98c2..3b5319dd93 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h | |||
| @@ -16,17 +16,27 @@ | |||
| 16 | #define Elf_Ehdr Elf32_Ehdr | 16 | #define Elf_Ehdr Elf32_Ehdr |
| 17 | #define Elf_Shdr Elf32_Shdr | 17 | #define Elf_Shdr Elf32_Shdr |
| 18 | #define Elf_Sym Elf32_Sym | 18 | #define Elf_Sym Elf32_Sym |
| 19 | #define Elf_Addr Elf32_Addr | ||
| 20 | #define Elf_Section Elf32_Section | ||
| 19 | #define ELF_ST_BIND ELF32_ST_BIND | 21 | #define ELF_ST_BIND ELF32_ST_BIND |
| 20 | #define ELF_ST_TYPE ELF32_ST_TYPE | 22 | #define ELF_ST_TYPE ELF32_ST_TYPE |
| 21 | 23 | ||
| 24 | #define Elf_Rela Elf32_Rela | ||
| 25 | #define ELF_R_SYM ELF32_R_SYM | ||
| 26 | #define ELF_R_TYPE ELF32_R_TYPE | ||
| 22 | #else | 27 | #else |
| 23 | 28 | ||
| 24 | #define Elf_Ehdr Elf64_Ehdr | 29 | #define Elf_Ehdr Elf64_Ehdr |
| 25 | #define Elf_Shdr Elf64_Shdr | 30 | #define Elf_Shdr Elf64_Shdr |
| 26 | #define Elf_Sym Elf64_Sym | 31 | #define Elf_Sym Elf64_Sym |
| 32 | #define Elf_Addr Elf64_Addr | ||
| 33 | #define Elf_Section Elf64_Section | ||
| 27 | #define ELF_ST_BIND ELF64_ST_BIND | 34 | #define ELF_ST_BIND ELF64_ST_BIND |
| 28 | #define ELF_ST_TYPE ELF64_ST_TYPE | 35 | #define ELF_ST_TYPE ELF64_ST_TYPE |
| 29 | 36 | ||
| 37 | #define Elf_Rela Elf64_Rela | ||
| 38 | #define ELF_R_SYM ELF64_R_SYM | ||
| 39 | #define ELF_R_TYPE ELF64_R_TYPE | ||
| 30 | #endif | 40 | #endif |
| 31 | 41 | ||
| 32 | #if KERNEL_ELFDATA != HOST_ELFDATA | 42 | #if KERNEL_ELFDATA != HOST_ELFDATA |
