diff options
author | Kees Cook <keescook@chromium.org> | 2013-04-12 16:13:43 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-04-16 18:19:13 -0400 |
commit | 5d442e63d6a1b5736fd48a907bd7d2d87e411816 (patch) | |
tree | 624bf6368b566e7b809ddd64324c1292f297ac2f /arch/x86/tools | |
parent | bf11655cf2ecdcfaacbc8324da4a3edfe276ba9d (diff) |
x86, relocs: Consolidate processing logic
Instead of counting and then processing relocations, do it in a single
pass. This splits the processing logic into separate functions for
realmode and 32-bit (and paves the way for 64-bit). Also extracts helper
functions when emitting relocations.
Based on work by Neill Clift and Michael Davidson.
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: http://lkml.kernel.org/r/1365797627-20874-3-git-send-email-keescook@chromium.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/tools')
-rw-r--r-- | arch/x86/tools/relocs.c | 304 |
1 files changed, 170 insertions, 134 deletions
diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c index fd28ef7dfbb9..bdc5930b3a1b 100644 --- a/arch/x86/tools/relocs.c +++ b/arch/x86/tools/relocs.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <stdarg.h> | 2 | #include <stdarg.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include <inttypes.h> | ||
5 | #include <string.h> | 6 | #include <string.h> |
6 | #include <errno.h> | 7 | #include <errno.h> |
7 | #include <unistd.h> | 8 | #include <unistd.h> |
@@ -38,10 +39,15 @@ static void die(char *fmt, ...); | |||
38 | 39 | ||
39 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | 40 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
40 | static Elf_Ehdr ehdr; | 41 | static Elf_Ehdr ehdr; |
41 | static unsigned long reloc_count, reloc_idx; | 42 | |
42 | static unsigned long *relocs; | 43 | struct relocs { |
43 | static unsigned long reloc16_count, reloc16_idx; | 44 | uint32_t *offset; |
44 | static unsigned long *relocs16; | 45 | unsigned long count; |
46 | unsigned long size; | ||
47 | }; | ||
48 | |||
49 | static struct relocs relocs16; | ||
50 | static struct relocs relocs32; | ||
45 | 51 | ||
46 | struct section { | 52 | struct section { |
47 | Elf_Shdr shdr; | 53 | Elf_Shdr shdr; |
@@ -583,8 +589,23 @@ static void print_absolute_relocs(void) | |||
583 | printf("\n"); | 589 | printf("\n"); |
584 | } | 590 | } |
585 | 591 | ||
586 | static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym), | 592 | static void add_reloc(struct relocs *r, uint32_t offset) |
587 | int use_real_mode) | 593 | { |
594 | if (r->count == r->size) { | ||
595 | unsigned long newsize = r->size + 50000; | ||
596 | void *mem = realloc(r->offset, newsize * sizeof(r->offset[0])); | ||
597 | |||
598 | if (!mem) | ||
599 | die("realloc of %ld entries for relocs failed\n", | ||
600 | newsize); | ||
601 | r->offset = mem; | ||
602 | r->size = newsize; | ||
603 | } | ||
604 | r->offset[r->count++] = offset; | ||
605 | } | ||
606 | |||
607 | static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel, | ||
608 | Elf_Sym *sym, const char *symname)) | ||
588 | { | 609 | { |
589 | int i; | 610 | int i; |
590 | /* Walk through the relocations */ | 611 | /* Walk through the relocations */ |
@@ -606,100 +627,142 @@ static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym), | |||
606 | sh_symtab = sec_symtab->symtab; | 627 | sh_symtab = sec_symtab->symtab; |
607 | sym_strtab = sec_symtab->link->strtab; | 628 | sym_strtab = sec_symtab->link->strtab; |
608 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { | 629 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { |
609 | Elf_Rel *rel; | 630 | Elf_Rel *rel = &sec->reltab[j]; |
610 | Elf_Sym *sym; | 631 | Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; |
611 | unsigned r_type; | 632 | const char *symname = sym_name(sym_strtab, sym); |
612 | const char *symname; | ||
613 | int shn_abs; | ||
614 | 633 | ||
615 | rel = &sec->reltab[j]; | 634 | process(sec, rel, sym, symname); |
616 | sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; | 635 | } |
617 | r_type = ELF_R_TYPE(rel->r_info); | 636 | } |
618 | 637 | } | |
619 | shn_abs = sym->st_shndx == SHN_ABS; | 638 | |
620 | 639 | static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, | |
621 | switch (r_type) { | 640 | const char *symname) |
622 | case R_386_NONE: | 641 | { |
623 | case R_386_PC32: | 642 | unsigned r_type = ELF32_R_TYPE(rel->r_info); |
624 | case R_386_PC16: | 643 | int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); |
625 | case R_386_PC8: | 644 | |
626 | /* | 645 | switch (r_type) { |
627 | * NONE can be ignored and and PC relative | 646 | case R_386_NONE: |
628 | * relocations don't need to be adjusted. | 647 | case R_386_PC32: |
629 | */ | 648 | case R_386_PC16: |
649 | case R_386_PC8: | ||
650 | /* | ||
651 | * NONE can be ignored and PC relative relocations don't | ||
652 | * need to be adjusted. | ||
653 | */ | ||
654 | break; | ||
655 | |||
656 | case R_386_32: | ||
657 | if (shn_abs) { | ||
658 | /* | ||
659 | * Whitelisted absolute symbols do not require | ||
660 | * relocation. | ||
661 | */ | ||
662 | if (is_reloc(S_ABS, symname)) | ||
630 | break; | 663 | break; |
631 | 664 | ||
632 | case R_386_16: | 665 | die("Invalid absolute %s relocation: %s\n", |
633 | symname = sym_name(sym_strtab, sym); | 666 | rel_type(r_type), symname); |
634 | if (!use_real_mode) | 667 | break; |
635 | goto bad; | 668 | } |
636 | if (shn_abs) { | 669 | |
637 | if (is_reloc(S_ABS, symname)) | 670 | add_reloc(&relocs32, rel->r_offset); |
638 | break; | 671 | break; |
639 | else if (!is_reloc(S_SEG, symname)) | 672 | |
640 | goto bad; | 673 | default: |
641 | } else { | 674 | die("Unsupported relocation type: %s (%d)\n", |
642 | if (is_reloc(S_LIN, symname)) | 675 | rel_type(r_type), r_type); |
643 | goto bad; | 676 | break; |
644 | else | 677 | } |
645 | break; | 678 | |
646 | } | 679 | return 0; |
647 | visit(rel, sym); | 680 | } |
681 | |||
682 | static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, | ||
683 | const char *symname) | ||
684 | { | ||
685 | unsigned r_type = ELF32_R_TYPE(rel->r_info); | ||
686 | int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); | ||
687 | |||
688 | switch (r_type) { | ||
689 | case R_386_NONE: | ||
690 | case R_386_PC32: | ||
691 | case R_386_PC16: | ||
692 | case R_386_PC8: | ||
693 | /* | ||
694 | * NONE can be ignored and PC relative relocations don't | ||
695 | * need to be adjusted. | ||
696 | */ | ||
697 | break; | ||
698 | |||
699 | case R_386_16: | ||
700 | if (shn_abs) { | ||
701 | /* | ||
702 | * Whitelisted absolute symbols do not require | ||
703 | * relocation. | ||
704 | */ | ||
705 | if (is_reloc(S_ABS, symname)) | ||
648 | break; | 706 | break; |
649 | 707 | ||
650 | case R_386_32: | 708 | if (is_reloc(S_SEG, symname)) { |
651 | symname = sym_name(sym_strtab, sym); | 709 | add_reloc(&relocs16, rel->r_offset); |
652 | if (shn_abs) { | 710 | break; |
653 | if (is_reloc(S_ABS, symname)) | 711 | } |
654 | break; | 712 | } else { |
655 | else if (!is_reloc(S_REL, symname)) | 713 | if (!is_reloc(S_LIN, symname)) |
656 | goto bad; | ||
657 | } else { | ||
658 | if (use_real_mode && | ||
659 | !is_reloc(S_LIN, symname)) | ||
660 | break; | ||
661 | } | ||
662 | visit(rel, sym); | ||
663 | break; | 714 | break; |
664 | default: | 715 | } |
665 | die("Unsupported relocation type: %s (%d)\n", | 716 | die("Invalid %s %s relocation: %s\n", |
666 | rel_type(r_type), r_type); | 717 | shn_abs ? "absolute" : "relative", |
718 | rel_type(r_type), symname); | ||
719 | break; | ||
720 | |||
721 | case R_386_32: | ||
722 | if (shn_abs) { | ||
723 | /* | ||
724 | * Whitelisted absolute symbols do not require | ||
725 | * relocation. | ||
726 | */ | ||
727 | if (is_reloc(S_ABS, symname)) | ||
728 | break; | ||
729 | |||
730 | if (is_reloc(S_REL, symname)) { | ||
731 | add_reloc(&relocs32, rel->r_offset); | ||
667 | break; | 732 | break; |
668 | bad: | ||
669 | symname = sym_name(sym_strtab, sym); | ||
670 | die("Invalid %s %s relocation: %s\n", | ||
671 | shn_abs ? "absolute" : "relative", | ||
672 | rel_type(r_type), symname); | ||
673 | } | 733 | } |
734 | } else { | ||
735 | if (is_reloc(S_LIN, symname)) | ||
736 | add_reloc(&relocs32, rel->r_offset); | ||
737 | break; | ||
674 | } | 738 | } |
675 | } | 739 | die("Invalid %s %s relocation: %s\n", |
676 | } | 740 | shn_abs ? "absolute" : "relative", |
741 | rel_type(r_type), symname); | ||
742 | break; | ||
677 | 743 | ||
678 | static void count_reloc(Elf_Rel *rel, Elf_Sym *sym) | 744 | default: |
679 | { | 745 | die("Unsupported relocation type: %s (%d)\n", |
680 | if (ELF_R_TYPE(rel->r_info) == R_386_16) | 746 | rel_type(r_type), r_type); |
681 | reloc16_count++; | 747 | break; |
682 | else | 748 | } |
683 | reloc_count++; | ||
684 | } | ||
685 | 749 | ||
686 | static void collect_reloc(Elf_Rel *rel, Elf_Sym *sym) | 750 | return 0; |
687 | { | ||
688 | /* Remember the address that needs to be adjusted. */ | ||
689 | if (ELF_R_TYPE(rel->r_info) == R_386_16) | ||
690 | relocs16[reloc16_idx++] = rel->r_offset; | ||
691 | else | ||
692 | relocs[reloc_idx++] = rel->r_offset; | ||
693 | } | 751 | } |
694 | 752 | ||
695 | static int cmp_relocs(const void *va, const void *vb) | 753 | static int cmp_relocs(const void *va, const void *vb) |
696 | { | 754 | { |
697 | const unsigned long *a, *b; | 755 | const uint32_t *a, *b; |
698 | a = va; b = vb; | 756 | a = va; b = vb; |
699 | return (*a == *b)? 0 : (*a > *b)? 1 : -1; | 757 | return (*a == *b)? 0 : (*a > *b)? 1 : -1; |
700 | } | 758 | } |
701 | 759 | ||
702 | static int write32(unsigned int v, FILE *f) | 760 | static void sort_relocs(struct relocs *r) |
761 | { | ||
762 | qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs); | ||
763 | } | ||
764 | |||
765 | static int write32(uint32_t v, FILE *f) | ||
703 | { | 766 | { |
704 | unsigned char buf[4]; | 767 | unsigned char buf[4]; |
705 | 768 | ||
@@ -707,33 +770,25 @@ static int write32(unsigned int v, FILE *f) | |||
707 | return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; | 770 | return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; |
708 | } | 771 | } |
709 | 772 | ||
773 | static int write32_as_text(uint32_t v, FILE *f) | ||
774 | { | ||
775 | return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1; | ||
776 | } | ||
777 | |||
710 | static void emit_relocs(int as_text, int use_real_mode) | 778 | static void emit_relocs(int as_text, int use_real_mode) |
711 | { | 779 | { |
712 | int i; | 780 | int i; |
713 | /* Count how many relocations I have and allocate space for them. */ | 781 | int (*write_reloc)(uint32_t, FILE *) = write32; |
714 | reloc_count = 0; | ||
715 | walk_relocs(count_reloc, use_real_mode); | ||
716 | relocs = malloc(reloc_count * sizeof(relocs[0])); | ||
717 | if (!relocs) { | ||
718 | die("malloc of %d entries for relocs failed\n", | ||
719 | reloc_count); | ||
720 | } | ||
721 | 782 | ||
722 | relocs16 = malloc(reloc16_count * sizeof(relocs[0])); | ||
723 | if (!relocs16) { | ||
724 | die("malloc of %d entries for relocs16 failed\n", | ||
725 | reloc16_count); | ||
726 | } | ||
727 | /* Collect up the relocations */ | 783 | /* Collect up the relocations */ |
728 | reloc_idx = 0; | 784 | walk_relocs(use_real_mode ? do_reloc_real : do_reloc); |
729 | walk_relocs(collect_reloc, use_real_mode); | ||
730 | 785 | ||
731 | if (reloc16_count && !use_real_mode) | 786 | if (relocs16.count && !use_real_mode) |
732 | die("Segment relocations found but --realmode not specified\n"); | 787 | die("Segment relocations found but --realmode not specified\n"); |
733 | 788 | ||
734 | /* Order the relocations for more efficient processing */ | 789 | /* Order the relocations for more efficient processing */ |
735 | qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); | 790 | sort_relocs(&relocs16); |
736 | qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs); | 791 | sort_relocs(&relocs32); |
737 | 792 | ||
738 | /* Print the relocations */ | 793 | /* Print the relocations */ |
739 | if (as_text) { | 794 | if (as_text) { |
@@ -742,43 +797,24 @@ static void emit_relocs(int as_text, int use_real_mode) | |||
742 | */ | 797 | */ |
743 | printf(".section \".data.reloc\",\"a\"\n"); | 798 | printf(".section \".data.reloc\",\"a\"\n"); |
744 | printf(".balign 4\n"); | 799 | printf(".balign 4\n"); |
745 | if (use_real_mode) { | 800 | write_reloc = write32_as_text; |
746 | printf("\t.long %lu\n", reloc16_count); | ||
747 | for (i = 0; i < reloc16_count; i++) | ||
748 | printf("\t.long 0x%08lx\n", relocs16[i]); | ||
749 | printf("\t.long %lu\n", reloc_count); | ||
750 | for (i = 0; i < reloc_count; i++) { | ||
751 | printf("\t.long 0x%08lx\n", relocs[i]); | ||
752 | } | ||
753 | } else { | ||
754 | /* Print a stop */ | ||
755 | printf("\t.long 0x%08lx\n", (unsigned long)0); | ||
756 | for (i = 0; i < reloc_count; i++) { | ||
757 | printf("\t.long 0x%08lx\n", relocs[i]); | ||
758 | } | ||
759 | } | ||
760 | |||
761 | printf("\n"); | ||
762 | } | 801 | } |
763 | else { | ||
764 | if (use_real_mode) { | ||
765 | write32(reloc16_count, stdout); | ||
766 | for (i = 0; i < reloc16_count; i++) | ||
767 | write32(relocs16[i], stdout); | ||
768 | write32(reloc_count, stdout); | ||
769 | |||
770 | /* Now print each relocation */ | ||
771 | for (i = 0; i < reloc_count; i++) | ||
772 | write32(relocs[i], stdout); | ||
773 | } else { | ||
774 | /* Print a stop */ | ||
775 | write32(0, stdout); | ||
776 | 802 | ||
777 | /* Now print each relocation */ | 803 | if (use_real_mode) { |
778 | for (i = 0; i < reloc_count; i++) { | 804 | write_reloc(relocs16.count, stdout); |
779 | write32(relocs[i], stdout); | 805 | for (i = 0; i < relocs16.count; i++) |
780 | } | 806 | write_reloc(relocs16.offset[i], stdout); |
781 | } | 807 | |
808 | write_reloc(relocs32.count, stdout); | ||
809 | for (i = 0; i < relocs32.count; i++) | ||
810 | write_reloc(relocs32.offset[i], stdout); | ||
811 | } else { | ||
812 | /* Print a stop */ | ||
813 | write_reloc(0, stdout); | ||
814 | |||
815 | /* Now print each relocation */ | ||
816 | for (i = 0; i < relocs32.count; i++) | ||
817 | write_reloc(relocs32.offset[i], stdout); | ||
782 | } | 818 | } |
783 | } | 819 | } |
784 | 820 | ||