diff options
-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 d901095ea8b7..a7360c379cba 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 c0de7b98c24c..3b5319dd937f 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 |