aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/tools
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2013-04-12 16:13:43 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2013-04-16 18:19:13 -0400
commit5d442e63d6a1b5736fd48a907bd7d2d87e411816 (patch)
tree624bf6368b566e7b809ddd64324c1292f297ac2f /arch/x86/tools
parentbf11655cf2ecdcfaacbc8324da4a3edfe276ba9d (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.c304
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]))
40static Elf_Ehdr ehdr; 41static Elf_Ehdr ehdr;
41static unsigned long reloc_count, reloc_idx; 42
42static unsigned long *relocs; 43struct relocs {
43static unsigned long reloc16_count, reloc16_idx; 44 uint32_t *offset;
44static unsigned long *relocs16; 45 unsigned long count;
46 unsigned long size;
47};
48
49static struct relocs relocs16;
50static struct relocs relocs32;
45 51
46struct section { 52struct 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
586static void walk_relocs(void (*visit)(Elf_Rel *rel, Elf_Sym *sym), 592static 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
607static 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 639static 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
682static 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
678static 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
686static 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
695static int cmp_relocs(const void *va, const void *vb) 753static 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
702static int write32(unsigned int v, FILE *f) 760static void sort_relocs(struct relocs *r)
761{
762 qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs);
763}
764
765static 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
773static 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
710static void emit_relocs(int as_text, int use_real_mode) 778static 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