modpost: Optimize symbol search from linear to binary search

[ Upstream commit 4074532758 ]

Modify modpost to use binary search for converting addresses back
into symbol references.  Previously it used linear search.

This change saves a few seconds of wall time for defconfig builds,
but can save several minutes on allyesconfigs.

Before:
$ make LLVM=1 -j128 allyesconfig vmlinux -s KCFLAGS="-Wno-error"
$ time scripts/mod/modpost -M -m -a -N -o vmlinux.symvers vmlinux.o
198.38user 1.27system 3:19.71elapsed

After:
$ make LLVM=1 -j128 allyesconfig vmlinux -s KCFLAGS="-Wno-error"
$ time scripts/mod/modpost -M -m -a -N -o vmlinux.symvers vmlinux.o
11.91user 0.85system 0:12.78elapsed

Signed-off-by: Jack Brennen <jbrennen@google.com>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Stable-dep-of: 1102f9f85b ("modpost: do not make find_tosym() return NULL")
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Jack Brennen
2023-09-26 08:40:44 -04:00
committed by Greg Kroah-Hartman
parent 55ed6c4778
commit 2bc92c61c5
4 changed files with 232 additions and 66 deletions

View File

@@ -22,7 +22,6 @@
#include <errno.h>
#include "modpost.h"
#include "../../include/linux/license.h"
#include "../../include/linux/module_symbol.h"
static bool module_enabled;
/* Are we using CONFIG_MODVERSIONS? */
@@ -577,11 +576,14 @@ static int parse_elf(struct elf_info *info, const char *filename)
*p = TO_NATIVE(*p);
}
symsearch_init(info);
return 1;
}
static void parse_elf_finish(struct elf_info *info)
{
symsearch_finish(info);
release_file(info->hdr, info->size);
}
@@ -1042,71 +1044,10 @@ static int secref_whitelist(const char *fromsec, const char *fromsym,
return 1;
}
/*
* If there's no name there, ignore it; likewise, ignore it if it's
* one of the magic symbols emitted used by current tools.
*
* Otherwise if find_symbols_between() returns those symbols, they'll
* fail the whitelist tests and cause lots of false alarms ... fixable
* only by merging __exit and __init sections into __text, bloating
* the kernel (which is especially evil on embedded platforms).
*/
static inline int is_valid_name(struct elf_info *elf, Elf_Sym *sym)
{
const char *name = elf->strtab + sym->st_name;
if (!name || !strlen(name))
return 0;
return !is_mapping_symbol(name);
}
/* Look up the nearest symbol based on the section and the address */
static Elf_Sym *find_nearest_sym(struct elf_info *elf, Elf_Addr addr,
unsigned int secndx, bool allow_negative,
Elf_Addr min_distance)
{
Elf_Sym *sym;
Elf_Sym *near = NULL;
Elf_Addr sym_addr, distance;
bool is_arm = (elf->hdr->e_machine == EM_ARM);
for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
if (get_secindex(elf, sym) != secndx)
continue;
if (!is_valid_name(elf, sym))
continue;
sym_addr = sym->st_value;
/*
* For ARM Thumb instruction, the bit 0 of st_value is set
* if the symbol is STT_FUNC type. Mask it to get the address.
*/
if (is_arm && ELF_ST_TYPE(sym->st_info) == STT_FUNC)
sym_addr &= ~1;
if (addr >= sym_addr)
distance = addr - sym_addr;
else if (allow_negative)
distance = sym_addr - addr;
else
continue;
if (distance <= min_distance) {
min_distance = distance;
near = sym;
}
if (min_distance == 0)
break;
}
return near;
}
static Elf_Sym *find_fromsym(struct elf_info *elf, Elf_Addr addr,
unsigned int secndx)
{
return find_nearest_sym(elf, addr, secndx, false, ~0);
return symsearch_find_nearest(elf, addr, secndx, false, ~0);
}
static Elf_Sym *find_tosym(struct elf_info *elf, Elf_Addr addr, Elf_Sym *sym)
@@ -1119,7 +1060,8 @@ static Elf_Sym *find_tosym(struct elf_info *elf, Elf_Addr addr, Elf_Sym *sym)
* Strive to find a better symbol name, but the resulting name may not
* match the symbol referenced in the original code.
*/
return find_nearest_sym(elf, addr, get_secindex(elf, sym), true, 20);
return symsearch_find_nearest(elf, addr, get_secindex(elf, sym),
true, 20);
}
static bool is_executable_section(struct elf_info *elf, unsigned int secndx)