objtool: Fix code relocs vs weak symbols

Occasionally objtool driven code patching (think .static_call_sites
.retpoline_sites etc..) goes sideways and it tries to patch an
instruction that doesn't match.

Much head-scatching and cursing later the problem is as outlined below
and affects every section that objtool generates for us, very much
including the ORC data. The below uses .static_call_sites because it's
convenient for demonstration purposes, but as mentioned the ORC
sections, .retpoline_sites and __mount_loc are all similarly affected.

Consider:

foo-weak.c:

extern void __SCT__foo(void);

__attribute__((weak)) void foo(void)
{
return __SCT__foo();
}

foo.c:

extern void __SCT__foo(void);
extern void my_foo(void);

void foo(void)
{
my_foo();
return __SCT__foo();
}

These generate the obvious code
(gcc -O2 -fcf-protection=none -fno-asynchronous-unwind-tables -c foo*.c):

foo-weak.o:
0000000000000000 <foo>:
0: e9 00 00 00 00 jmpq 5 <foo+0x5> 1: R_X86_64_PLT32 __SCT__foo-0x4

foo.o:
0000000000000000 <foo>:
0: 48 83 ec 08 sub $0x8,%rsp
4: e8 00 00 00 00 callq 9 <foo+0x9> 5: R_X86_64_PLT32 my_foo-0x4
9: 48 83 c4 08 add $0x8,%rsp
d: e9 00 00 00 00 jmpq 12 <foo+0x12> e: R_X86_64_PLT32 __SCT__foo-0x4

Now, when we link these two files together, you get something like
(ld -r -o foos.o foo-weak.o foo.o):

foos.o:
0000000000000000 <foo-0x10>:
0: e9 00 00 00 00 jmpq 5 <foo-0xb> 1: R_X86_64_PLT32 __SCT__foo-0x4
5: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:0x0(%rax,%rax,1)
f: 90 nop

0000000000000010 <foo>:
10: 48 83 ec 08 sub $0x8,%rsp
14: e8 00 00 00 00 callq 19 <foo+0x9> 15: R_X86_64_PLT32 my_foo-0x4
19: 48 83 c4 08 add $0x8,%rsp
1d: e9 00 00 00 00 jmpq 22 <foo+0x12> 1e: R_X86_64_PLT32 __SCT__foo-0x4

Noting that ld preserves the weak function text, but strips the symbol
off of it (hence objdump doing that funny negative offset thing). This
does lead to 'interesting' unused code issues with objtool when ran on
linked objects, but that seems to be working (fingers crossed).

So far so good.. Now lets consider the objtool static_call output
section (readelf output, old binutils):

foo-weak.o:

Relocation section '.rela.static_call_sites' at offset 0x2c8 contains 1 entry:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000200000002 R_X86_64_PC32 0000000000000000 .text + 0
0000000000000004 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1

foo.o:

Relocation section '.rela.static_call_sites' at offset 0x310 contains 2 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000200000002 R_X86_64_PC32 0000000000000000 .text + d
0000000000000004 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1

foos.o:

Relocation section '.rela.static_call_sites' at offset 0x430 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000100000002 R_X86_64_PC32 0000000000000000 .text + 0
0000000000000004 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1
0000000000000008 0000000100000002 R_X86_64_PC32 0000000000000000 .text + 1d
000000000000000c 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1

So we have two patch sites, one in the dead code of the weak foo and one
in the real foo. All is well.

*HOWEVER*, when the toolchain strips unused section symbols it
generates things like this (using new enough binutils):

foo-weak.o:

Relocation section '.rela.static_call_sites' at offset 0x2c8 contains 1 entry:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000200000002 R_X86_64_PC32 0000000000000000 foo + 0
0000000000000004 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1

foo.o:

Relocation section '.rela.static_call_sites' at offset 0x310 contains 2 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000200000002 R_X86_64_PC32 0000000000000000 foo + d
0000000000000004 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1

foos.o:

Relocation section '.rela.static_call_sites' at offset 0x430 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000100000002 R_X86_64_PC32 0000000000000000 foo + 0
0000000000000004 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1
0000000000000008 0000000100000002 R_X86_64_PC32 0000000000000000 foo + d
000000000000000c 0000000d00000002 R_X86_64_PC32 0000000000000000 __SCT__foo + 1

And now we can see how that foos.o .static_call_sites goes side-ways, we
now have _two_ patch sites in foo. One for the weak symbol at foo+0
(which is no longer a static_call site!) and one at foo+d which is in
fact the right location.

This seems to happen when objtool cannot find a section symbol, in which
case it falls back to any other symbol to key off of, however in this
case that goes terribly wrong!

As such, teach objtool to create a section symbol when there isn't
one.

Fixes: 44f6a7c0755d ("objtool: Fix seg fault with Clang non-section symbols")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lkml.kernel.org/r/20220419203807.655552918@infradead.org

+165 -22
+165 -22
tools/objtool/elf.c
··· 575 575 return 0; 576 576 } 577 577 578 + /* 579 + * Ensure that any reloc section containing references to @sym is marked 580 + * changed such that it will get re-generated in elf_rebuild_reloc_sections() 581 + * with the new symbol index. 582 + */ 583 + static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym) 584 + { 585 + struct section *sec; 586 + 587 + list_for_each_entry(sec, &elf->sections, list) { 588 + struct reloc *reloc; 589 + 590 + if (sec->changed) 591 + continue; 592 + 593 + list_for_each_entry(reloc, &sec->reloc_list, list) { 594 + if (reloc->sym == sym) { 595 + sec->changed = true; 596 + break; 597 + } 598 + } 599 + } 600 + } 601 + 602 + /* 603 + * Move the first global symbol, as per sh_info, into a new, higher symbol 604 + * index. This fees up the shndx for a new local symbol. 605 + */ 606 + static int elf_move_global_symbol(struct elf *elf, struct section *symtab, 607 + struct section *symtab_shndx) 608 + { 609 + Elf_Data *data, *shndx_data = NULL; 610 + Elf32_Word first_non_local; 611 + struct symbol *sym; 612 + Elf_Scn *s; 613 + 614 + first_non_local = symtab->sh.sh_info; 615 + 616 + sym = find_symbol_by_index(elf, first_non_local); 617 + if (!sym) { 618 + WARN("no non-local symbols !?"); 619 + return first_non_local; 620 + } 621 + 622 + s = elf_getscn(elf->elf, symtab->idx); 623 + if (!s) { 624 + WARN_ELF("elf_getscn"); 625 + return -1; 626 + } 627 + 628 + data = elf_newdata(s); 629 + if (!data) { 630 + WARN_ELF("elf_newdata"); 631 + return -1; 632 + } 633 + 634 + data->d_buf = &sym->sym; 635 + data->d_size = sizeof(sym->sym); 636 + data->d_align = 1; 637 + data->d_type = ELF_T_SYM; 638 + 639 + sym->idx = symtab->sh.sh_size / sizeof(sym->sym); 640 + elf_dirty_reloc_sym(elf, sym); 641 + 642 + symtab->sh.sh_info += 1; 643 + symtab->sh.sh_size += data->d_size; 644 + symtab->changed = true; 645 + 646 + if (symtab_shndx) { 647 + s = elf_getscn(elf->elf, symtab_shndx->idx); 648 + if (!s) { 649 + WARN_ELF("elf_getscn"); 650 + return -1; 651 + } 652 + 653 + shndx_data = elf_newdata(s); 654 + if (!shndx_data) { 655 + WARN_ELF("elf_newshndx_data"); 656 + return -1; 657 + } 658 + 659 + shndx_data->d_buf = &sym->sec->idx; 660 + shndx_data->d_size = sizeof(Elf32_Word); 661 + shndx_data->d_align = 4; 662 + shndx_data->d_type = ELF_T_WORD; 663 + 664 + symtab_shndx->sh.sh_size += 4; 665 + symtab_shndx->changed = true; 666 + } 667 + 668 + return first_non_local; 669 + } 670 + 671 + static struct symbol * 672 + elf_create_section_symbol(struct elf *elf, struct section *sec) 673 + { 674 + struct section *symtab, *symtab_shndx; 675 + Elf_Data *shndx_data = NULL; 676 + struct symbol *sym; 677 + Elf32_Word shndx; 678 + 679 + symtab = find_section_by_name(elf, ".symtab"); 680 + if (symtab) { 681 + symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); 682 + if (symtab_shndx) 683 + shndx_data = symtab_shndx->data; 684 + } else { 685 + WARN("no .symtab"); 686 + return NULL; 687 + } 688 + 689 + sym = malloc(sizeof(*sym)); 690 + if (!sym) { 691 + perror("malloc"); 692 + return NULL; 693 + } 694 + memset(sym, 0, sizeof(*sym)); 695 + 696 + sym->idx = elf_move_global_symbol(elf, symtab, symtab_shndx); 697 + if (sym->idx < 0) { 698 + WARN("elf_move_global_symbol"); 699 + return NULL; 700 + } 701 + 702 + sym->name = sec->name; 703 + sym->sec = sec; 704 + 705 + // st_name 0 706 + sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION); 707 + // st_other 0 708 + // st_value 0 709 + // st_size 0 710 + shndx = sec->idx; 711 + if (shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) { 712 + sym->sym.st_shndx = shndx; 713 + if (!shndx_data) 714 + shndx = 0; 715 + } else { 716 + sym->sym.st_shndx = SHN_XINDEX; 717 + if (!shndx_data) { 718 + WARN("no .symtab_shndx"); 719 + return NULL; 720 + } 721 + } 722 + 723 + if (!gelf_update_symshndx(symtab->data, shndx_data, sym->idx, &sym->sym, shndx)) { 724 + WARN_ELF("gelf_update_symshndx"); 725 + return NULL; 726 + } 727 + 728 + elf_add_symbol(elf, sym); 729 + 730 + return sym; 731 + } 732 + 578 733 int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, 579 734 unsigned long offset, unsigned int type, 580 735 struct section *insn_sec, unsigned long insn_off) 581 736 { 582 - struct symbol *sym; 583 - int addend; 737 + struct symbol *sym = insn_sec->sym; 738 + int addend = insn_off; 584 739 585 - if (insn_sec->sym) { 586 - sym = insn_sec->sym; 587 - addend = insn_off; 588 - 589 - } else { 740 + if (!sym) { 590 741 /* 591 - * The Clang assembler strips section symbols, so we have to 592 - * reference the function symbol instead: 742 + * Due to how weak functions work, we must use section based 743 + * relocations. Symbol based relocations would result in the 744 + * weak and non-weak function annotations being overlaid on the 745 + * non-weak function after linking. 593 746 */ 594 - sym = find_symbol_containing(insn_sec, insn_off); 595 - if (!sym) { 596 - /* 597 - * Hack alert. This happens when we need to reference 598 - * the NOP pad insn immediately after the function. 599 - */ 600 - sym = find_symbol_containing(insn_sec, insn_off - 1); 601 - } 602 - 603 - if (!sym) { 604 - WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off); 747 + sym = elf_create_section_symbol(elf, insn_sec); 748 + if (!sym) 605 749 return -1; 606 - } 607 750 608 - addend = insn_off - sym->offset; 751 + insn_sec->sym = sym; 609 752 } 610 753 611 754 return elf_add_reloc(elf, sec, offset, type, sym, addend);