diff options
| -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 | ||
