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

objtool: Speed up SHT_GROUP reindexing

After elf_update_group_sh_info() was introduced, a prototype version of
"objtool klp diff" went from taking ~1s to several minutes, due to
looping almost endlessly in elf_update_group_sh_info() while creating
thousands of local symbols in a file with thousands of sections.

Dramatically improve the performance by marking all symbols' correlated
SHT_GROUP sections while reading the object. That way there's no need
to search for it every time a symbol gets reindexed.

Fixes: 2cb291596e2c ("objtool: Fix up st_info in COMDAT group section")
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Rong Xu <xur@google.com>
Link: https://lkml.kernel.org/r/2a33e583c87e3283706f346f9d59aac20653b7fd.1746662991.git.jpoimboe@kernel.org

authored by

Josh Poimboeuf and committed by
Peter Zijlstra
4ed9d82b 2cb29159

+30 -18
+29 -18
tools/objtool/elf.c
··· 572 572 return -1; 573 573 } 574 574 575 - /* 576 - * @sym's idx has changed. Update the sh_info in group sections. 577 - */ 578 - static void elf_update_group_sh_info(struct elf *elf, Elf32_Word symtab_idx, 579 - Elf32_Word new_idx, Elf32_Word old_idx) 575 + static int mark_group_syms(struct elf *elf) 580 576 { 581 - struct section *sec; 577 + struct section *symtab, *sec; 578 + struct symbol *sym; 579 + 580 + symtab = find_section_by_name(elf, ".symtab"); 581 + if (!symtab) { 582 + ERROR("no .symtab"); 583 + return -1; 584 + } 582 585 583 586 list_for_each_entry(sec, &elf->sections, list) { 584 - if (sec->sh.sh_type != SHT_GROUP) 585 - continue; 586 - if (sec->sh.sh_link == symtab_idx && 587 - sec->sh.sh_info == old_idx) { 588 - sec->sh.sh_info = new_idx; 589 - mark_sec_changed(elf, sec, true); 590 - /* 591 - * Each ELF group should have a unique symbol key. 592 - * Return early on match. 593 - */ 594 - return; 587 + if (sec->sh.sh_type == SHT_GROUP && 588 + sec->sh.sh_link == symtab->idx) { 589 + sym = find_symbol_by_index(elf, sec->sh.sh_info); 590 + if (!sym) { 591 + ERROR("%s: can't find SHT_GROUP signature symbol", 592 + sec->name); 593 + return -1; 594 + } 595 + 596 + sym->group_sec = sec; 595 597 } 596 598 } 599 + 600 + return 0; 597 601 } 598 602 599 603 /* ··· 791 787 if (elf_update_sym_relocs(elf, old)) 792 788 return NULL; 793 789 794 - elf_update_group_sh_info(elf, symtab->idx, new_idx, first_non_local); 790 + if (old->group_sec) { 791 + old->group_sec->sh.sh_info = new_idx; 792 + mark_sec_changed(elf, old->group_sec, true); 793 + } 794 + 795 795 new_idx = first_non_local; 796 796 } 797 797 ··· 1066 1058 goto err; 1067 1059 1068 1060 if (read_symbols(elf)) 1061 + goto err; 1062 + 1063 + if (mark_group_syms(elf)) 1069 1064 goto err; 1070 1065 1071 1066 if (read_relocs(elf))
+1
tools/objtool/include/objtool/elf.h
··· 72 72 u8 ignore : 1; 73 73 struct list_head pv_target; 74 74 struct reloc *relocs; 75 + struct section *group_sec; 75 76 }; 76 77 77 78 struct reloc {