Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86, realmode: 16-bit real-mode code support for relocs tool

A new option is added to the relocs tool called '--realmode'.
This option causes the generation of 16-bit segment relocations
and 32-bit linear relocations for the real-mode code. When
the real-mode code is moved to the low-memory during kernel
initialization, these relocation entries can be used to
relocate the code properly.

In the assembly code 16-bit segment relocations must be relative
to the 'real_mode_seg' absolute symbol. Linear relocations must be
relative to a symbol prefixed with 'pa_'.

16-bit segment relocation is used to load cs:ip in 16-bit code.
Linear relocations are used in the 32-bit code for relocatable
data references. They are declared in the linker script of the
real-mode code.

The relocs tool is moved to scripts/x86-relocs.c so it will
be compiled before building the arch/x86 tree.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Link: http://lkml.kernel.org/r/1336501366-28617-2-git-send-email-jarkko.sakkinen@intel.com
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>

+185 -63
+5 -6
arch/x86/boot/compressed/Makefile
··· 40 40 $(obj)/vmlinux.bin: vmlinux FORCE 41 41 $(call if_changed,objcopy) 42 42 43 + targets += vmlinux.bin.all vmlinux.relocs 43 44 44 - targets += vmlinux.bin.all vmlinux.relocs relocs 45 - hostprogs-$(CONFIG_X86_NEED_RELOCS) += relocs 46 - 47 - quiet_cmd_relocs = RELOCS $@ 48 - cmd_relocs = $(obj)/relocs $< > $@;$(obj)/relocs --abs-relocs $< 49 - $(obj)/vmlinux.relocs: vmlinux $(obj)/relocs FORCE 45 + CMD_RELOCS = scripts/x86-relocs 46 + quiet_cmd_relocs = RELOCS $@ 47 + cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $< 48 + $(obj)/vmlinux.relocs: vmlinux FORCE 50 49 $(call if_changed,relocs) 51 50 52 51 vmlinux.bin.all-y := $(obj)/vmlinux.bin
+176 -57
arch/x86/boot/compressed/relocs.c scripts/x86-relocs.c
··· 18 18 static Elf32_Ehdr ehdr; 19 19 static unsigned long reloc_count, reloc_idx; 20 20 static unsigned long *relocs; 21 + static unsigned long reloc16_count, reloc16_idx; 22 + static unsigned long *relocs16; 21 23 22 24 struct section { 23 25 Elf32_Shdr shdr; ··· 30 28 }; 31 29 static struct section *secs; 32 30 31 + enum symtype { 32 + S_ABS, 33 + S_REL, 34 + S_SEG, 35 + S_LIN, 36 + S_NSYMTYPES 37 + }; 38 + 39 + static const char * const sym_regex_kernel[S_NSYMTYPES] = { 33 40 /* 34 41 * Following symbols have been audited. There values are constant and do 35 42 * not change if bzImage is loaded at a different physical address than 36 43 * the address for which it has been compiled. Don't warn user about 37 44 * absolute relocations present w.r.t these symbols. 38 45 */ 39 - static const char abs_sym_regex[] = 46 + [S_ABS] = 40 47 "^(xen_irq_disable_direct_reloc$|" 41 48 "xen_save_fl_direct_reloc$|" 42 49 "VDSO|" 43 - "__crc_)"; 44 - static regex_t abs_sym_regex_c; 45 - static int is_abs_reloc(const char *sym_name) 46 - { 47 - return !regexec(&abs_sym_regex_c, sym_name, 0, NULL, 0); 48 - } 50 + "__crc_)", 49 51 50 52 /* 51 53 * These symbols are known to be relative, even if the linker marks them 52 54 * as absolute (typically defined outside any section in the linker script.) 53 55 */ 54 - static const char rel_sym_regex[] = 55 - "^_end$"; 56 - static regex_t rel_sym_regex_c; 57 - static int is_rel_reloc(const char *sym_name) 56 + [S_REL] = 57 + "^_end$", 58 + }; 59 + 60 + 61 + static const char * const sym_regex_realmode[S_NSYMTYPES] = { 62 + /* 63 + * These are 16-bit segment symbols when compiling 16-bit code. 64 + */ 65 + [S_SEG] = 66 + "^real_mode_seg$", 67 + 68 + /* 69 + * These are offsets belonging to segments, as opposed to linear addresses, 70 + * when compiling 16-bit code. 71 + */ 72 + [S_LIN] = 73 + "^pa_", 74 + }; 75 + 76 + static const char * const *sym_regex; 77 + 78 + static regex_t sym_regex_c[S_NSYMTYPES]; 79 + static int is_reloc(enum symtype type, const char *sym_name) 58 80 { 59 - return !regexec(&rel_sym_regex_c, sym_name, 0, NULL, 0); 81 + return sym_regex[type] && 82 + !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); 60 83 } 61 84 62 - static void regex_init(void) 85 + static void regex_init(int use_real_mode) 63 86 { 64 87 char errbuf[128]; 65 88 int err; 66 - 67 - err = regcomp(&abs_sym_regex_c, abs_sym_regex, 68 - REG_EXTENDED|REG_NOSUB); 69 - if (err) { 70 - regerror(err, &abs_sym_regex_c, errbuf, sizeof errbuf); 71 - die("%s", errbuf); 72 - } 89 + int i; 73 90 74 - err = regcomp(&rel_sym_regex_c, rel_sym_regex, 75 - REG_EXTENDED|REG_NOSUB); 76 - if (err) { 77 - regerror(err, &rel_sym_regex_c, errbuf, sizeof errbuf); 78 - die("%s", errbuf); 91 + if (use_real_mode) 92 + sym_regex = sym_regex_realmode; 93 + else 94 + sym_regex = sym_regex_kernel; 95 + 96 + for (i = 0; i < S_NSYMTYPES; i++) { 97 + if (!sym_regex[i]) 98 + continue; 99 + 100 + err = regcomp(&sym_regex_c[i], sym_regex[i], 101 + REG_EXTENDED|REG_NOSUB); 102 + 103 + if (err) { 104 + regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); 105 + die("%s", errbuf); 106 + } 79 107 } 80 108 } 81 109 ··· 186 154 REL_TYPE(R_386_RELATIVE), 187 155 REL_TYPE(R_386_GOTOFF), 188 156 REL_TYPE(R_386_GOTPC), 157 + REL_TYPE(R_386_8), 158 + REL_TYPE(R_386_PC8), 159 + REL_TYPE(R_386_16), 160 + REL_TYPE(R_386_PC16), 189 161 #undef REL_TYPE 190 162 }; 191 163 const char *name = "unknown type rel type name"; ··· 225 189 name = sym_strtab + sym->st_name; 226 190 } 227 191 else { 228 - name = sec_name(secs[sym->st_shndx].shdr.sh_name); 192 + name = sec_name(sym->st_shndx); 229 193 } 230 194 return name; 231 195 } ··· 508 472 * Before warning check if this absolute symbol 509 473 * relocation is harmless. 510 474 */ 511 - if (is_abs_reloc(name) || is_rel_reloc(name)) 475 + if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) 512 476 continue; 513 477 514 478 if (!printed) { ··· 532 496 printf("\n"); 533 497 } 534 498 535 - static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) 499 + static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym), 500 + int use_real_mode) 536 501 { 537 502 int i; 538 503 /* Walk through the relocations */ ··· 558 521 Elf32_Rel *rel; 559 522 Elf32_Sym *sym; 560 523 unsigned r_type; 524 + const char *symname; 561 525 rel = &sec->reltab[j]; 562 526 sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; 563 527 r_type = ELF32_R_TYPE(rel->r_info); 564 - /* Don't visit relocations to absolute symbols */ 565 - if (sym->st_shndx == SHN_ABS && 566 - !is_rel_reloc(sym_name(sym_strtab, sym))) { 567 - continue; 568 - } 528 + 569 529 switch (r_type) { 570 530 case R_386_NONE: 571 531 case R_386_PC32: 532 + case R_386_PC16: 533 + case R_386_PC8: 572 534 /* 573 535 * NONE can be ignored and and PC relative 574 536 * relocations don't need to be adjusted. 575 537 */ 576 538 break; 539 + 540 + case R_386_16: 541 + symname = sym_name(sym_strtab, sym); 542 + if (!use_real_mode) 543 + goto bad; 544 + if (sym->st_shndx == SHN_ABS) { 545 + if (is_reloc(S_ABS, symname)) 546 + break; 547 + else if (!is_reloc(S_SEG, symname)) 548 + goto bad; 549 + } else { 550 + if (is_reloc(S_LIN, symname)) 551 + goto bad; 552 + else 553 + break; 554 + } 555 + visit(rel, sym); 556 + break; 557 + 577 558 case R_386_32: 578 - /* Visit relocations that need to be adjusted */ 559 + symname = sym_name(sym_strtab, sym); 560 + if (sym->st_shndx == SHN_ABS) { 561 + if (is_reloc(S_ABS, symname)) 562 + break; 563 + else if (!is_reloc(S_REL, symname)) 564 + goto bad; 565 + } else { 566 + if (use_real_mode && 567 + !is_reloc(S_LIN, symname)) 568 + break; 569 + } 579 570 visit(rel, sym); 580 571 break; 581 572 default: 582 573 die("Unsupported relocation type: %s (%d)\n", 583 574 rel_type(r_type), r_type); 584 575 break; 576 + bad: 577 + symname = sym_name(sym_strtab, sym); 578 + die("Invalid %s relocation: %s\n", 579 + rel_type(r_type), symname); 585 580 } 586 581 } 587 582 } ··· 621 552 622 553 static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) 623 554 { 624 - reloc_count += 1; 555 + if (ELF32_R_TYPE(rel->r_info) == R_386_16) 556 + reloc16_count++; 557 + else 558 + reloc_count++; 625 559 } 626 560 627 561 static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) 628 562 { 629 563 /* Remember the address that needs to be adjusted. */ 630 - relocs[reloc_idx++] = rel->r_offset; 564 + if (ELF32_R_TYPE(rel->r_info) == R_386_16) 565 + relocs16[reloc16_idx++] = rel->r_offset; 566 + else 567 + relocs[reloc_idx++] = rel->r_offset; 631 568 } 632 569 633 570 static int cmp_relocs(const void *va, const void *vb) ··· 643 568 return (*a == *b)? 0 : (*a > *b)? 1 : -1; 644 569 } 645 570 646 - static void emit_relocs(int as_text) 571 + static int write32(unsigned int v, FILE *f) 572 + { 573 + unsigned char buf[4]; 574 + 575 + put_unaligned_le32(v, buf); 576 + return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; 577 + } 578 + 579 + static void emit_relocs(int as_text, int use_real_mode) 647 580 { 648 581 int i; 649 582 /* Count how many relocations I have and allocate space for them. */ 650 583 reloc_count = 0; 651 - walk_relocs(count_reloc); 584 + walk_relocs(count_reloc, use_real_mode); 652 585 relocs = malloc(reloc_count * sizeof(relocs[0])); 653 586 if (!relocs) { 654 587 die("malloc of %d entries for relocs failed\n", 655 588 reloc_count); 656 589 } 590 + 591 + relocs16 = malloc(reloc16_count * sizeof(relocs[0])); 592 + if (!relocs16) { 593 + die("malloc of %d entries for relocs16 failed\n", 594 + reloc16_count); 595 + } 657 596 /* Collect up the relocations */ 658 597 reloc_idx = 0; 659 - walk_relocs(collect_reloc); 598 + walk_relocs(collect_reloc, use_real_mode); 599 + 600 + if (reloc16_count && !use_real_mode) 601 + die("Segment relocations found but --realmode not specified\n"); 660 602 661 603 /* Order the relocations for more efficient processing */ 662 604 qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); 605 + qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs); 663 606 664 607 /* Print the relocations */ 665 608 if (as_text) { ··· 686 593 */ 687 594 printf(".section \".data.reloc\",\"a\"\n"); 688 595 printf(".balign 4\n"); 689 - for (i = 0; i < reloc_count; i++) { 690 - printf("\t .long 0x%08lx\n", relocs[i]); 596 + if (use_real_mode) { 597 + printf("\t.long %lu\n", reloc16_count); 598 + for (i = 0; i < reloc16_count; i++) 599 + printf("\t.long 0x%08lx\n", relocs16[i]); 600 + printf("\t.long %lu\n", reloc_count); 601 + for (i = 0; i < reloc_count; i++) { 602 + printf("\t.long 0x%08lx\n", relocs[i]); 603 + } 604 + } else { 605 + /* Print a stop */ 606 + printf("\t.long 0x%08lx\n", (unsigned long)0); 607 + for (i = 0; i < reloc_count; i++) { 608 + printf("\t.long 0x%08lx\n", relocs[i]); 609 + } 691 610 } 611 + 692 612 printf("\n"); 693 613 } 694 614 else { 695 - unsigned char buf[4]; 696 - /* Print a stop */ 697 - fwrite("\0\0\0\0", 4, 1, stdout); 698 - /* Now print each relocation */ 699 - for (i = 0; i < reloc_count; i++) { 700 - put_unaligned_le32(relocs[i], buf); 701 - fwrite(buf, 4, 1, stdout); 615 + if (use_real_mode) { 616 + write32(reloc16_count, stdout); 617 + for (i = 0; i < reloc16_count; i++) 618 + write32(relocs16[i], stdout); 619 + write32(reloc_count, stdout); 620 + 621 + /* Now print each relocation */ 622 + for (i = 0; i < reloc_count; i++) 623 + write32(relocs[i], stdout); 624 + } else { 625 + /* Print a stop */ 626 + write32(0, stdout); 627 + 628 + /* Now print each relocation */ 629 + for (i = 0; i < reloc_count; i++) { 630 + write32(relocs[i], stdout); 631 + } 702 632 } 703 633 } 704 634 } 705 635 706 636 static void usage(void) 707 637 { 708 - die("relocs [--abs-syms |--abs-relocs | --text] vmlinux\n"); 638 + die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n"); 709 639 } 710 640 711 641 int main(int argc, char **argv) 712 642 { 713 643 int show_absolute_syms, show_absolute_relocs; 714 - int as_text; 644 + int as_text, use_real_mode; 715 645 const char *fname; 716 646 FILE *fp; 717 647 int i; 718 648 719 - regex_init(); 720 - 721 649 show_absolute_syms = 0; 722 650 show_absolute_relocs = 0; 723 651 as_text = 0; 652 + use_real_mode = 0; 724 653 fname = NULL; 725 654 for (i = 1; i < argc; i++) { 726 655 char *arg = argv[i]; 727 656 if (*arg == '-') { 728 - if (strcmp(argv[1], "--abs-syms") == 0) { 657 + if (strcmp(arg, "--abs-syms") == 0) { 729 658 show_absolute_syms = 1; 730 659 continue; 731 660 } 732 - 733 - if (strcmp(argv[1], "--abs-relocs") == 0) { 661 + if (strcmp(arg, "--abs-relocs") == 0) { 734 662 show_absolute_relocs = 1; 735 663 continue; 736 664 } 737 - else if (strcmp(argv[1], "--text") == 0) { 665 + if (strcmp(arg, "--text") == 0) { 738 666 as_text = 1; 667 + continue; 668 + } 669 + if (strcmp(arg, "--realmode") == 0) { 670 + use_real_mode = 1; 739 671 continue; 740 672 } 741 673 } ··· 773 655 if (!fname) { 774 656 usage(); 775 657 } 658 + regex_init(use_real_mode); 776 659 fp = fopen(fname, "r"); 777 660 if (!fp) { 778 661 die("Cannot open %s: %s\n", ··· 792 673 print_absolute_relocs(); 793 674 return 0; 794 675 } 795 - emit_relocs(as_text); 676 + emit_relocs(as_text, use_real_mode); 796 677 return 0; 797 678 }
+1
scripts/.gitignore
··· 9 9 ihex2fw 10 10 recordmcount 11 11 docproc 12 + x86-relocs
+3
scripts/Makefile
··· 8 8 # conmakehash: Create arrays for initializing the kernel console tables 9 9 # docproc: Used in Documentation/DocBook 10 10 11 + HOST_EXTRACFLAGS += -I$(srctree)/tools/include 12 + 11 13 hostprogs-$(CONFIG_KALLSYMS) += kallsyms 12 14 hostprogs-$(CONFIG_LOGO) += pnmtologo 13 15 hostprogs-$(CONFIG_VT) += conmakehash 14 16 hostprogs-$(CONFIG_IKCONFIG) += bin2c 15 17 hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount 18 + hostprogs-$(CONFIG_X86) += x86-relocs 16 19 17 20 always := $(hostprogs-y) $(hostprogs-m) 18 21