Objtool updates for v7.3:

- Fix various klp-build bugs reported by Joe Lawrence
    (Josh Poimboeuf, Joe Lawrence)
 
  - Misc fixes and cleanups (Puranjay Mohan, Thomas Huth and Ingo Molnar).
 
 Signed-off-by: Ingo Molnar <mingo@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmqCz54RHG1pbmdvQGtl
 cm5lbC5vcmcACgkQEnMQ0APhK1gD/A/8DRBAjlv+aj6knJgmAP6GZI6Wc2pfjj1C
 5UPUZQf9/ib+/19D2YfOEvUM2T5RZg9fy2OhPGM3Qbe8GiCbKKpB1quJg3U7JB0P
 Z1jO7ViSt0n1ako04xWfDAOjRld32u7idDu/RD+qw5Sahzsy/HPejnIZS6AXYe8V
 Jw1KDviPfx3d2xPAcizp92Zg9SywcPcBI+jQVn61+ly1ZNbVTvUOlsARIWYOB4LI
 wTfeNt3G1rbrmODsm6bXcz7y+anuO8OmjYJbZKoGraiXwnbuZewocBXK00NPDAVx
 tuxnbwQ0g+dQ8gNQBL9bqEG/jeJzG7h/2n5s5/EnGV4SelQC/m+HPK/E40aHbwjv
 tCLIe9rVYXRpiMVgfR4xUDfTVM3EPiu6chIVPFYG8YDXZ0lMdUccGZEZEAIuflxT
 sSdHJT5P1ARzzuVHriPe3DS/jXEcd7UTVW99jcis3Xbl4TlfhQC5X2IueBfSYEFj
 hSMizFzaJJ1S7wC5x7lO6nNQklyzivLB1fARVT84uYCfdKcxPleobT6mSggW290Z
 u6kznFOLhiFt2eGAcWEEPzgIp9PjMKpXlHXzZnCl8TJyXVvwUGZgE6xEzKjJGa0B
 ZsGJyeJUwpnKPfS/1dULsFub/zTtviVIluCWs0teWhp5FAdbkGXGRBF8K1CqZpOw
 5tOwtn/kIDU=
 =4+gg
 -----END PGP SIGNATURE-----

Merge tag 'objtool-core-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molnar:

 - Fix various klp-build bugs reported by Joe Lawrence (Josh Poimboeuf,
   Joe Lawrence)

 - Misc fixes and cleanups (Puranjay Mohan, Thomas Huth and Ingo Molnar)

* tag 'objtool-core-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  objtool/klp: Fix vmlinux klp relocations for EXPORT_SYMBOL_FOR_MODULES()
  objtool/klp: Fix .kcfi_traps special section extraction
  objtool/klp: Fix vmlinux .klp.symid link error for .exitcall.exit symbols
  objtool/klp: Fix line numbers in Module.symvers parse errors
  objtool/klp: Fix relocations for EXPORT_SYMBOL_FOR_MODULES() symbols
  objtool/klp: Allow new references to module exports
  objtool/klp: Don't match local symbols against exports
  objtool/klp: Fix cross-module klp relocation section naming
  objtool/klp: Explicitly disallow patching or referencing init code/data
  objtool/klp: Ignore replacement offset of empty x86 alternatives
  objtool/klp: Fix size of empty special section entries
  objtool/klp: Fix vmlinux .klp.symid link error for .no_trim_symbol symbols
  objtool/headers: Sync tools/include/linux/objtool_types.h with include/linux/objtool_types.h
  objtool: Replace __ASSEMBLY__ with __ASSEMBLER__ in header files
  objtool/klp: Fix symbol resolution for duplicate data symbols
  objtool/klp: Add .klp.symid for sympos disambiguation
  objtool/klp: Skip hidden directories when finding objects
  objtool/klp: Fix false module dependencies caused by dead relocs
  objtool/klp: Normalize Module.symvers paths to module names
  objtool/klp: Fix module name normalization for paths with dots
This commit is contained in:
Linus Torvalds 2026-08-18 12:18:33 -07:00
commit a0acd94e38
20 changed files with 877 additions and 132 deletions

View File

@ -839,12 +839,20 @@
.stab.index 0 : { *(.stab.index) } \
.stab.indexstr 0 : { *(.stab.indexstr) }
#ifdef CONFIG_KLP_BUILD
#define KLP_SYMID \
.klp.symid 0 : { *(.klp.symid) }
#else
#define KLP_SYMID
#endif
/* Required sections not related to debugging. */
#define ELF_DETAILS \
.comment 0 : { *(.comment) } \
.symtab 0 : { *(.symtab) } \
.strtab 0 : { *(.strtab) } \
.shstrtab 0 : { *(.shstrtab) }
.shstrtab 0 : { *(.shstrtab) } \
KLP_SYMID
#define MODINFO \
.modinfo : { *(.modinfo) . = ALIGN(8); }

View File

@ -7,7 +7,7 @@
#ifdef CONFIG_OBJTOOL
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#define UNWIND_HINT(type, sp_reg, sp_offset, signal) \
"987: \n\t" \
@ -53,7 +53,7 @@
#define __ASM_BREF(label) label ## b
#else /* __ASSEMBLY__ */
#else /* __ASSEMBLER__ */
/*
* In asm, there are two kinds of code: normal C-type callable functions and
@ -102,11 +102,11 @@
#endif
.endm
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
#else /* !CONFIG_OBJTOOL */
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t"
#define STACK_FRAME_NON_STANDARD(func)

View File

@ -2,7 +2,7 @@
#ifndef _LINUX_OBJTOOL_TYPES_H
#define _LINUX_OBJTOOL_TYPES_H
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#include <linux/types.h>
@ -18,7 +18,7 @@ struct unwind_hint {
u8 signal;
};
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
/*
* UNWIND_HINT_TYPE_UNDEFINED: A blind spot in ORC coverage which can result in

View File

@ -47,6 +47,9 @@ endif
vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
$(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
# Only used for builds initiated by klp-build
vmlinux-objtool-args-$(if $(KLP_SYMIDS),y) += --klp-symids
objtool-args = $(vmlinux-objtool-args-y) --link
# Link of vmlinux.o used for section mismatch analysis

View File

@ -271,6 +271,9 @@ validate_config() {
[[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \
die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported"
[[ -v CONFIG_LD_DEAD_CODE_DATA_ELIMINATION ]] && \
die "kernel option 'CONFIG_LD_DEAD_CODE_DATA_ELIMINATION' not supported"
[[ -v CONFIG_AS_IS_LLVM ]] && \
[[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
die "Clang assembler version < 20 not supported"
@ -555,6 +558,8 @@ build_kernel() {
#
cmd+=("KBUILD_MODPOST_WARN=1")
cmd+=("KLP_SYMIDS=1")
if [[ -v VERBOSE ]]; then
cmd+=("V=1")
else
@ -575,8 +580,9 @@ find_objects() {
local opts=("$@")
# Find root-level vmlinux.o and non-root-level .ko files,
# excluding klp-tmp/ and .git/
find "$PWD" \( -path "$TMP_DIR" -o -path "$PWD/.git" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
# excluding klp-tmp/ and hidden directories.
find "$PWD" -mindepth 1 \
\( -path "$TMP_DIR" -o -name ".*" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
-type f "${opts[@]}" \
\( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
-printf '%P\n'
@ -605,6 +611,8 @@ copy_orig_objects() {
done
xtrace_restore
cp -f "$PWD/vmlinux" "$ORIG_DIR" || die "missing vmlinux"
mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
touch "$TIMESTAMP"
touch "$ORIG_DIR/.complete"
@ -675,6 +683,8 @@ generate_checksums() {
"$OBJTOOL" klp checksum "$dest"
done
[[ -f "$src_dir/vmlinux" ]] && cp -f "$src_dir/vmlinux" "$dest_dir"
touch "$dest_dir/.complete"
}

View File

@ -772,6 +772,7 @@ static const char *const section_white_list[] =
".llvm.call-graph-profile", /* call graph */
"__llvm_covfun",
"__llvm_covmap",
".klp.symid", /* objtool --klp-symids */
NULL
};

View File

@ -2,7 +2,7 @@
#ifndef _LINUX_OBJTOOL_TYPES_H
#define _LINUX_OBJTOOL_TYPES_H
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#include <linux/types.h>
@ -18,7 +18,7 @@ struct unwind_hint {
u8 signal;
};
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
/*
* UNWIND_HINT_TYPE_UNDEFINED: A blind spot in ORC coverage which can result in

View File

@ -6,13 +6,15 @@ objtool-y += check.o
objtool-y += special.o
objtool-y += builtin-check.o
objtool-y += elf.o
objtool-y += klp-symid.o
objtool-y += objtool.o
objtool-$(BUILD_DISAS) += disas.o
objtool-$(BUILD_DISAS) += trace.o
objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
objtool-$(BUILD_KLP) += builtin-klp.o klp-checksum.o klp-diff.o klp-post-link.o
objtool-$(BUILD_KLP) += builtin-klp.o klp-checksum.o klp-diff.o \
klp-post-link.o klp-sympos.o
objtool-y += libstring.o
objtool-y += libctype.o

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string.h>
#include <arch/special.h>
#include <objtool/special.h>
#include <objtool/builtin.h>
#include <objtool/warn.h>
@ -9,6 +10,32 @@
/* cpu feature name array generated from cpufeatures.h */
#include "cpu-feature-names.c"
/*
* An alternative with an empty replacement, e.g. the second entry of
*
* ALTERNATIVE_2("orig", "repl", ft1, "", ft2)
*
* still gets a relocation for its replacement offset. But the label it points
* at is the end of the previous entry's replacement, which is also the
* beginning of the *next* entry's replacement. The value is meaningless: it's
* only ever used with a length of zero.
*/
bool arch_alt_ignore_new_reloc(struct section *sec, unsigned long offset)
{
unsigned long entry_off;
if (strcmp(sec->name, ".altinstructions"))
return false;
entry_off = offset - (offset % ALT_ENTRY_SIZE);
if (offset - entry_off != ALT_NEW_OFFSET)
return false;
return !*(unsigned char *)(sec->data->d_buf + entry_off +
ALT_NEW_LEN_OFFSET);
}
void arch_handle_alternative(struct special_alt *alt)
{
static struct special_alt *group, *prev;

View File

@ -76,6 +76,7 @@ static const struct option check_options[] = {
OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
OPT_BOOLEAN(0, "klp-symids", &opts.klp_symids, "generate .klp.symids for duplicate symbol disambiguation"),
OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
OPT_BOOLEAN(0, "noabs", &opts.noabs, "reject absolute references in allocatable sections"),
OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
@ -174,10 +175,16 @@ static bool opts_valid(void)
return false;
}
if (opts.klp_symids && !opts.link) {
ERROR("--klp-symids requires --link");
return false;
}
if (opts.disas ||
opts.hack_jump_label ||
opts.hack_noinstr ||
opts.ibt ||
opts.klp_symids ||
opts.mcount ||
opts.noabs ||
opts.noinstr ||

View File

@ -15,6 +15,7 @@
#include <objtool/arch.h>
#include <objtool/disas.h>
#include <objtool/check.h>
#include <objtool/klp.h>
#include <objtool/special.h>
#include <objtool/trace.h>
#include <objtool/warn.h>
@ -4924,6 +4925,12 @@ int check(struct objtool_file *file)
goto out;
}
if (opts.klp_symids) {
ret = klp_create_symid_sections(file);
if (ret)
goto out;
}
if (opts.noabs)
warnings += check_abs_references(file);

View File

@ -23,6 +23,7 @@
#include <linux/log2.h>
#include <objtool/builtin.h>
#include <objtool/elf.h>
#include <objtool/klp.h>
#include <objtool/warn.h>
static ssize_t demangled_name_len(const char *name);
@ -626,6 +627,18 @@ static int read_symbols(struct elf *elf)
return -1;
}
/*
* "klp diff" renames the placeholder symbols of KLP relocs to
* hide them from modpost. Hide the prefix from the rest of
* objtool so its many name-based heuristics (noreturns,
* uaccess safe list, ...) still see the original symbol name.
*
* st_name is left alone, so the renamed symbol is preserved in
* the output file.
*/
if (strstarts(sym->name, KLP_TOMBSTONE_PREFIX))
sym->name += strlen(KLP_TOMBSTONE_PREFIX);
if ((sym->sym.st_shndx > SHN_UNDEF &&
sym->sym.st_shndx < SHN_LORESERVE) ||
(shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {

View File

@ -16,6 +16,7 @@ struct opts {
bool hack_noinstr;
bool hack_skylake;
bool ibt;
bool klp_symids;
bool mcount;
bool noabs;
bool noinstr;

View File

@ -97,6 +97,7 @@ struct symbol {
u8 included : 1;
u8 klp : 1;
u8 dont_correlate : 1;
u8 fake : 1;
struct list_head pv_target;
struct reloc *relocs;
struct section *group_sec;

View File

@ -14,21 +14,48 @@
#define KLP_FUNCS_SEC ".init.klp_funcs"
/*
* __klp_relocs is an intermediate section which are created by klp diff and
* converted into KLP symbols/relas by "objtool klp post-link". This is needed
* to work around the linker, which doesn't preserve SHN_LIVEPATCH or
* __klp_relocs.<objname> are intermediate sections which are created by klp
* diff and converted into KLP symbols/relas by "objtool klp post-link". This
* is needed to work around the linker, which doesn't preserve SHN_LIVEPATCH or
* SHF_RELA_LIVEPATCH, nor does it support having two RELA sections for a
* single PROGBITS section.
*
* "objname" is the object whose loading gates the relocation: "vmlinux" for
* references to vmlinux symbols, otherwise the name of the module being
* patched. post-link uses it to name the resulting
* .klp.rela.objname.section_name sections.
*/
#define KLP_RELOCS_SEC "__klp_relocs"
#define KLP_STRINGS_SEC ".rodata.klp.str1.1"
#define KLP_TOMBSTONE_PREFIX ".klp.tombstone."
struct klp_reloc {
void *offset;
void *sym;
u32 type;
};
/*
* .klp.symid is used to correlate symbols between vmlinux.o and vmlinux, for
* calculating sympos to disambiguate duplicately-named symbols.
*/
#define KLP_SYMID_SEC ".klp.symid"
struct klp_symid {
u64 id;
u64 addr;
};
struct objtool_file;
struct elf;
struct symbol;
int klp_create_symid_sections(struct objtool_file *file);
int klp_sympos_init(struct elf *orig);
unsigned long klp_find_sympos(struct elf *elf, struct symbol *sym);
int cmd_klp_checksum(int argc, const char **argv);
int cmd_klp_diff(int argc, const char **argv);
int cmd_klp_post_link(int argc, const char **argv);

View File

@ -32,6 +32,13 @@ int special_get_alts(struct elf *elf, struct list_head *alts);
void arch_handle_alternative(struct special_alt *alt);
/*
* Should the reloc at @offset -- the "new" (replacement) field of a special
* section group entry -- be ignored? The meaning of a zero-length replacement
* is arch specific, so the arch decides.
*/
bool arch_alt_ignore_new_reloc(struct section *sec, unsigned long offset);
bool arch_support_alt_relocation(struct special_alt *special_alt,
struct instruction *insn,
struct reloc *reloc);

View File

@ -12,7 +12,7 @@
#include <objtool/arch.h>
#include <objtool/klp.h>
#include <objtool/util.h>
#include <arch/special.h>
#include <objtool/special.h>
#include <linux/align.h>
#include <linux/objtool_types.h>
@ -30,7 +30,9 @@ struct elfs {
struct export {
struct hlist_node hash;
char *mod, *sym;
char *mod;
char *sym;
bool mod_ns;
};
bool debug, debug_correlate, debug_clone;
@ -83,11 +85,40 @@ static char *escape_str(const char *orig)
return new;
}
/*
* Convert a build-tree object path to a runtime module name: strip
* directory components, replace '-' with '_', and remove file
* extensions. Examples:
*
* "arch/x86/kvm/kvm" -> "kvm"
* "arch/x86/kvm/kvm-intel" -> "kvm_intel".
*
* Used by read_exports() to normalize Module.symvers entries and by
* __find_modname() as a fallback when .modinfo lacks a "name=" tag.
*/
static char *normalize_modname(char *name)
{
char *slash = strrchr(name, '/');
if (slash)
name = slash + 1;
for (char *c = name; *c; c++) {
if (*c == '-')
*c = '_';
else if (*c == '.') {
*c = '\0';
break;
}
}
return name;
}
static int read_exports(void)
{
const char *symvers = "Module.symvers";
char line[1024], *path = NULL;
unsigned int line_num = 1;
unsigned int line_num = 0;
FILE *file;
file = fopen(symvers, "r");
@ -106,9 +137,11 @@ static int read_exports(void)
}
while (fgets(line, 1024, file)) {
char *sym, *mod, *type;
char *sym, *mod, *type, *namespace;
struct export *export;
line_num++;
sym = strchr(line, '\t');
if (!sym) {
ERROR("malformed Module.symvers (sym) at line %d", line_num);
@ -133,6 +166,14 @@ static int read_exports(void)
*type++ = '\0';
namespace = strchr(type, '\t');
if (!namespace) {
ERROR("malformed Module.symvers (namespace) at line %d", line_num);
return -1;
}
*namespace++ = '\0';
if (*sym == '\0' || *mod == '\0') {
ERROR("malformed Module.symvers at line %d", line_num);
return -1;
@ -150,12 +191,18 @@ static int read_exports(void)
return -1;
}
if (strcmp(export->mod, "vmlinux"))
export->mod = normalize_modname(export->mod);
export->sym = strdup(sym);
if (!export->sym) {
ERROR_GLIBC("strdup");
return -1;
}
/* EXPORT_SYMBOL_FOR_MODULES() */
export->mod_ns = strstarts(namespace, "module:");
hash_add(exports, &export->hash, str_hash(sym));
}
@ -866,65 +913,6 @@ static int correlate_symbols(struct elfs *e)
return 0;
}
/* "sympos" is used by livepatch to disambiguate duplicate symbol names */
static unsigned long find_sympos(struct elf *elf, struct symbol *sym)
{
bool vmlinux = str_ends_with(objname, "vmlinux.o");
unsigned long sympos = 0, nr_matches = 0;
bool has_dup = false;
struct symbol *s;
if (sym->bind != STB_LOCAL)
return 0;
if (vmlinux && is_func_sym(sym)) {
/*
* HACK: Unfortunately, symbol ordering can differ between
* vmlinux.o and vmlinux due to the linker script emitting
* .text.unlikely* before .text*. Count .text.unlikely* first.
*
* TODO: Disambiguate symbols more reliably (checksums?)
*/
for_each_sym(elf, s) {
if (strstarts(s->sec->name, ".text.unlikely") &&
!strcmp(s->name, sym->name)) {
nr_matches++;
if (s == sym)
sympos = nr_matches;
else
has_dup = true;
}
}
for_each_sym(elf, s) {
if (!strstarts(s->sec->name, ".text.unlikely") &&
!strcmp(s->name, sym->name)) {
nr_matches++;
if (s == sym)
sympos = nr_matches;
else
has_dup = true;
}
}
} else {
for_each_sym(elf, s) {
if (!strcmp(s->name, sym->name)) {
nr_matches++;
if (s == sym)
sympos = nr_matches;
else
has_dup = true;
}
}
}
if (!sympos) {
ERROR("can't find sympos for %s", sym->name);
return ULONG_MAX;
}
return has_dup ? sympos : 0;
}
static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym);
static struct symbol *__clone_symbol(struct elf *elf, struct symbol *patched_sym,
@ -1129,6 +1117,9 @@ static struct export *find_export(struct symbol *sym)
{
struct export *export;
if (is_local_sym(sym))
return NULL;
hash_for_each_possible(exports, export, hash, str_hash(sym->name)) {
if (!strcmp(export->sym, sym->name))
return export;
@ -1158,18 +1149,7 @@ static const char *__find_modname(struct elfs *e)
return NULL;
}
for (char *c = name; *c; c++) {
if (*c == '/')
name = c + 1;
else if (*c == '-')
*c = '_';
else if (*c == '.') {
*c = '\0';
break;
}
}
return name;
return normalize_modname(name);
}
/* Get the object's module name as defined by the kernel (and klp_object) */
@ -1210,11 +1190,16 @@ static bool klp_reloc_needed(struct reloc *patched_reloc)
* clusterfunk that is late module patching, the patch module is
* allowed to be loaded before any modules it depends on.
*
* If exported by vmlinux, a normal reloc will do.
* If exported by vmlinux to all modules, a normal reloc will do.
*/
export = find_export(patched_sym);
if (export)
return strcmp(export->mod, "vmlinux");
if (export) {
if (strcmp(export->mod, "vmlinux"))
return true;
/* EXPORT_SYMBOL_FOR_MODULES() gets a klp reloc */
return export->mod_ns;
}
if (!patched_sym->twin) {
/*
@ -1330,6 +1315,28 @@ static int convert_reloc_sym(struct elf *elf, struct reloc *reloc)
return convert_reloc_secsym_to_sym(elf, reloc);
}
/*
* Check if the original module already has a dependency on dep_mod, i.e. it
* already references at least one export from that module.
*/
static bool has_module_dep(struct elfs *e, const char *dep_mod)
{
struct symbol *sym;
for_each_sym(e->orig, sym) {
struct export *exp;
if (!is_undef_sym(sym) || is_weak_sym(sym))
continue;
exp = find_export(sym);
if (exp && !strcmp(exp->mod, dep_mod))
return true;
}
return false;
}
/*
* Convert a regular relocation to a klp relocation (sort of).
*/
@ -1337,33 +1344,51 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
struct section *sec, unsigned long offset,
struct export *export)
{
const char *sym_modname, *sym_orig_name, *sec_objname;
struct symbol *patched_sym = patched_reloc->sym;
s64 addend = reloc_addend(patched_reloc);
const char *sym_modname, *sym_orig_name;
static struct section *klp_relocs;
char tombstone_name[SYM_NAME_LEN];
struct symbol *sym, *klp_sym;
unsigned long klp_reloc_off;
struct section *klp_relocs;
char sec_name[SEC_NAME_LEN];
char sym_name[SYM_NAME_LEN];
struct klp_reloc klp_reloc;
unsigned long sympos;
if (!patched_sym->twin) {
ERROR("unexpected klp reloc for new symbol %s", patched_sym->name);
return -1;
if (!export) {
ERROR("unexpected klp reloc for new symbol %s", patched_sym->name);
return -1;
}
if (strcmp(export->mod, "vmlinux") &&
!has_module_dep(e, export->mod)) {
ERROR("%s: new reference to %s (exported by %s) would create an undeclared module dependency",
patched_sym->name, export->sym, export->mod);
return -1;
}
}
/*
* Keep the original reloc intact for now to avoid breaking objtool run
* which relies on proper relocations for many of its features. This
* will be disabled later by "objtool klp post-link".
* reloc now targets a functionally dead tombstone symbol and will be
* disabled later by "objtool klp post-link".
*
* Convert it to UNDEF (and WEAK to avoid modpost warnings).
* Convert the symbol to UNDEF/WEAK and rename to
* .klp.tombstone.sym_name to prevent modpost from printing warnings or
* creating false module dependencies. The prefix is hidden from the
* objtool run itself by read_symbols().
*/
sym = patched_sym->clone;
if (!sym) {
/* STB_WEAK: avoid modpost undefined symbol warnings */
sym = elf_create_symbol(e->out, patched_sym->name, NULL,
if (snprintf_check(tombstone_name, SYM_NAME_LEN,
KLP_TOMBSTONE_PREFIX "%s", patched_sym->name))
return -1;
sym = elf_create_symbol(e->out, tombstone_name, NULL,
STB_WEAK, patched_sym->type, 0, 0);
if (!sym)
return -1;
@ -1389,7 +1414,7 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
return -1;
sym_orig_name = patched_sym->twin->name;
sympos = find_sympos(e->orig, patched_sym->twin);
sympos = klp_find_sympos(e->orig, patched_sym->twin);
if (sympos == ULONG_MAX)
return -1;
}
@ -1411,16 +1436,35 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
}
/*
* Create the __klp_relocs entry. This will be converted to an actual
* KLP rela by "objtool klp post-link".
* Create the __klp_relocs.<objname> entry. This will be converted to
* an actual KLP rela by "objtool klp post-link".
*
* This intermediate step is necessary to prevent corruption by the
* linker, which doesn't know how to properly handle two rela sections
* applying to the same base section.
*
* The objname decides when the reloc gets applied. A reference to a
* vmlinux symbol goes in the vmlinux section so it gets applied when
* the patch module loads. Everything else goes in the patched
* object's section, applied when the patched module is loaded.
*/
if (!strcmp(sym_modname, "vmlinux")) {
sec_objname = "vmlinux";
} else {
sec_objname = find_modname(e);
if (!sec_objname)
return -1;
}
/* section format: __klp_relocs.objname */
if (snprintf_check(sec_name, SEC_NAME_LEN,
KLP_RELOCS_SEC ".%s", sec_objname))
return -1;
klp_relocs = find_section_by_name(e->out, sec_name);
if (!klp_relocs) {
klp_relocs = elf_create_section(e->out, KLP_RELOCS_SEC, 0,
klp_relocs = elf_create_section(e->out, sec_name, 0,
0, SHT_PROGBITS, 8, SHF_ALLOC);
if (!klp_relocs)
return -1;
@ -1568,6 +1612,10 @@ static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym)
!strcmp(patched_reloc->sym->sec->name, ".altinstr_aux"))
continue;
if (arch_alt_ignore_new_reloc(patched_sym->sec,
reloc_offset(patched_reloc)))
continue;
ret = convert_reloc_sym(e->patched, patched_reloc);
if (ret < 0) {
ERROR_FUNC(patched_rsec->base, reloc_offset(patched_reloc),
@ -1591,6 +1639,7 @@ static int create_fake_symbol(struct elf *elf, struct section *sec,
unsigned long offset, size_t size)
{
char name[SYM_NAME_LEN];
struct symbol *sym;
unsigned int type;
static int ctr;
char *c;
@ -1607,7 +1656,24 @@ static int create_fake_symbol(struct elf *elf, struct section *sec,
* while still allowing objdump to disassemble it.
*/
type = is_text_sec(sec) ? STT_NOTYPE : STT_OBJECT;
return elf_create_symbol(elf, name, sec, STB_LOCAL, type, offset, size) ? 0 : -1;
sym = elf_create_symbol(elf, name, sec, STB_LOCAL, type, offset, size);
if (!sym)
return -1;
sym->fake = 1;
return 0;
}
static bool has_fake_symbols(struct section *sec)
{
struct symbol *sym;
sec_for_each_sym(sec, sym)
if (sym->fake)
return true;
return false;
}
/*
@ -1658,13 +1724,17 @@ static int create_fake_symbols(struct elf *elf)
for_each_reloc(sec->rsec, reloc) {
unsigned long offset, size;
struct reloc *next_reloc;
bool last = true;
if (annotype(elf, sec, reloc) != ANNOTYPE_DATA_SPECIAL)
continue;
offset = reloc_addend(reloc);
size = 0;
/*
* Find the start of the next entry so the fake symbol size can
* be calculated.
*/
next_reloc = reloc;
for_each_reloc_continue(sec->rsec, next_reloc) {
if (annotype(elf, sec, next_reloc) != ANNOTYPE_DATA_SPECIAL ||
@ -1672,10 +1742,15 @@ static int create_fake_symbols(struct elf *elf)
continue;
size = reloc_addend(next_reloc) - offset;
last = false;
break;
}
if (!size)
/*
* If no next entry found, this is the last entry, so its size
* is from the current offset to the end of the section.
*/
if (last)
size = sec_size(reloc->sym->sec) - offset;
if (create_fake_symbol(elf, reloc->sym->sec, offset, size))
@ -1690,7 +1765,11 @@ static int create_fake_symbols(struct elf *elf)
unsigned int entry_size;
unsigned long offset;
if (!is_special_section(sec) || find_symbol_by_offset(sec, 0))
if (!is_special_section(sec))
continue;
/* Skip sections already handled by step 1 above */
if (has_fake_symbols(sec))
continue;
if (!sec->rsec) {
@ -2007,7 +2086,7 @@ static int create_klp_sections(struct elfs *e)
/* klp_func_ext.sympos */
BUILD_BUG_ON(sizeof(sympos) != sizeof_field(struct klp_func_ext, sympos));
sympos = find_sympos(e->orig, sym->clone->twin);
sympos = klp_find_sympos(e->orig, sym->clone->twin);
if (sympos == ULONG_MAX)
return -1;
memcpy(func_data + offsetof(struct klp_func_ext, sympos), &sympos,
@ -2161,6 +2240,9 @@ int cmd_klp_diff(int argc, const char **argv)
if (!e.orig || !e.patched)
return -1;
if (klp_sympos_init(e.orig))
return -1;
if (read_exports())
return -1;

View File

@ -19,19 +19,11 @@
#include <objtool/util.h>
#include <linux/livepatch_external.h>
static int fix_klp_relocs(struct elf *elf)
static int fix_klp_reloc_sec(struct elf *elf, struct section *symtab,
struct section *klp_relocs)
{
struct section *symtab, *klp_relocs;
klp_relocs = find_section_by_name(elf, KLP_RELOCS_SEC);
if (!klp_relocs)
return 0;
symtab = find_section_by_name(elf, ".symtab");
if (!symtab) {
ERROR("missing .symtab");
return -1;
}
/* section format: __klp_relocs.sec_objname */
const char *sec_objname = klp_relocs->name + strlen(KLP_RELOCS_SEC ".");
for (int i = 0; i < sec_size(klp_relocs) / sizeof(struct klp_reloc); i++) {
struct klp_reloc *klp_reloc;
@ -39,7 +31,6 @@ static int fix_klp_relocs(struct elf *elf)
struct section *sec, *tmp, *klp_rsec;
unsigned long offset;
struct reloc *reloc;
char sym_modname[64];
char rsec_name[SEC_NAME_LEN];
u64 addend;
struct symbol *sym, *klp_sym;
@ -55,7 +46,7 @@ static int fix_klp_relocs(struct elf *elf)
reloc = find_reloc_by_dest(elf, klp_relocs,
klp_reloc_off + offsetof(struct klp_reloc, offset));
if (!reloc) {
ERROR("malformed " KLP_RELOCS_SEC " section");
ERROR("malformed %s section", klp_relocs->name);
return -1;
}
@ -66,17 +57,13 @@ static int fix_klp_relocs(struct elf *elf)
reloc = find_reloc_by_dest(elf, klp_relocs,
klp_reloc_off + offsetof(struct klp_reloc, sym));
if (!reloc) {
ERROR("malformed " KLP_RELOCS_SEC " section");
ERROR("malformed %s section", klp_relocs->name);
return -1;
}
klp_sym = reloc->sym;
addend = reloc_addend(reloc);
/* symbol format: .klp.sym.modname.sym_name,sympos */
if (sscanf(klp_sym->name + strlen(KLP_SYM_PREFIX), "%55[^.]", sym_modname) != 1)
ERROR("can't find modname in klp symbol '%s'", klp_sym->name);
/*
* Create the KLP rela:
*/
@ -84,7 +71,7 @@ static int fix_klp_relocs(struct elf *elf)
/* section format: .klp.rela.sec_objname.section_name */
if (snprintf_check(rsec_name, SEC_NAME_LEN,
KLP_RELOC_SEC_PREFIX "%s.%s",
sym_modname, sec->name))
sec_objname, sec->name))
return -1;
klp_rsec = find_section_by_name(elf, rsec_name);
@ -134,10 +121,32 @@ static int fix_klp_relocs(struct elf *elf)
return 0;
}
static int fix_klp_relocs(struct elf *elf)
{
struct section *symtab, *sec;
symtab = find_section_by_name(elf, ".symtab");
if (!symtab) {
ERROR("missing .symtab");
return -1;
}
for_each_sec(elf, sec) {
if (strncmp(sec->name, KLP_RELOCS_SEC ".",
strlen(KLP_RELOCS_SEC ".")))
continue;
if (fix_klp_reloc_sec(elf, symtab, sec))
return -1;
}
return 0;
}
/*
* This runs on the livepatch module after all other linking has been done. It
* converts the intermediate __klp_relocs section into proper KLP relocs to be
* processed by livepatch. This needs to run last to avoid linker wreckage.
* converts the intermediate __klp_relocs.* sections into proper KLP relocs to
* be processed by livepatch. This needs to run last to avoid linker wreckage.
* Linkers don't tend to handle the "two rela sections for a single base
* section" case very well, nor do they appreciate SHN_LIVEPATCH.
*/

119
tools/objtool/klp-symid.c Normal file
View File

@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Emit the .klp.symid table which allows "objtool klp diff" to reliably
* disambiguate duplicate-named local symbols in vmlinux.
*
* Livepatch identifies a duplicate-named symbol by its position (sympos)
* among the same-named kallsyms entries, counted in ascending address order
* in the final linked vmlinux. That order can't be derived from vmlinux.o
* alone: the final link reorders sub-sections (.text.unlikely*, .data..*,
* etc).
*
* Bridge the gap with a table which survives the final link: a single
* non-alloc section containing an array of { id, addr } entries, where
* 'id' is a unique counter identifier and 'addr' has a relocation to the
* symbol. The linker copies 'id' verbatim and resolves 'addr' to the symbol's
* final address.
*
* The table is only emitted for vmlinux.o, and only when klp-build asks for it
* with KLP_SYMIDS=1, which adds --klp-symids to the vmlinux.o objtool run.
*
* It can't survive --gc-sections, which sweeps the whole section; klp-build
* rejects CONFIG_LD_DEAD_CODE_DATA_ELIMINATION.
*/
#include <linux/string.h>
#include <objtool/objtool.h>
#include <objtool/warn.h>
#include <objtool/endianness.h>
#include <objtool/klp.h>
static const char * const discarded_secs[] = {
".discard",
".exitcall.exit",
".modinfo",
".no_trim_symbol",
"__tracepoint_check",
};
static bool discarded_sec(struct section *sec)
{
if (!(sec->sh.sh_flags & SHF_ALLOC))
return true;
for (int i = 0; i < ARRAY_SIZE(discarded_secs); i++)
if (strstarts(sec->name, discarded_secs[i]))
return true;
return false;
}
static bool symid_needed(struct elf *elf, struct symbol *sym)
{
struct symbol *s;
if (!is_local_sym(sym) || is_undef_sym(sym))
return false;
if (!is_func_sym(sym) && !is_object_sym(sym))
return false;
if (is_prefix_func(sym))
return false;
if (discarded_sec(sym->sec))
return false;
for_each_sym_by_name(elf, sym->name, s) {
if (s == sym || is_sec_sym(s) || is_file_sym(s) || is_undef_sym(s))
continue;
return true;
}
return false;
}
int klp_create_symid_sections(struct objtool_file *file)
{
struct elf *elf = file->elf;
struct klp_symid *symids;
struct section *sec;
struct symbol *sym;
u64 nr = 0, i = 0;
if (!str_ends_with(objname, "vmlinux.o"))
return 0;
for_each_sym(elf, sym)
if (symid_needed(elf, sym))
nr++;
if (!nr)
return 0;
sec = elf_create_section(elf, KLP_SYMID_SEC, 0, sizeof(struct klp_symid),
SHT_PROGBITS, 8, 0);
if (!sec)
return -1;
symids = elf_add_data(elf, sec, NULL, nr * sizeof(struct klp_symid));
if (!symids)
return -1;
for_each_sym(elf, sym) {
if (!symid_needed(elf, sym))
continue;
symids[i].id = bswap_if_needed(elf, i);
if (!elf_create_reloc(elf, sec,
i * sizeof(struct klp_symid) +
offsetof(struct klp_symid, addr),
sym, 0, R_ABS64))
return -1;
i++;
}
return 0;
}

421
tools/objtool/klp-sympos.c Normal file
View File

@ -0,0 +1,421 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Compute "sympos", the position used by livepatch to disambiguate
* duplicate symbol names in the patched object.
*/
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <objtool/objtool.h>
#include <objtool/warn.h>
#include <objtool/endianness.h>
#include <objtool/klp.h>
#include <linux/string.h>
struct vmlinux_sym {
struct hlist_node hash;
const char *name;
u64 addr;
};
struct vmlinux_symid {
struct hlist_node hash;
u64 id;
u64 addr;
};
struct vmlinux_o_symid {
struct hlist_node hash;
u64 id;
unsigned int sym_idx;
};
static DEFINE_HASHTABLE(vmlinux_o_symids, 16);
/*
* The original linked kernel, found next to the orig vmlinux.o. Read with raw
* libelf rather than elf_open_read(): only the symbol table and the resolved
* .klp.symid table are needed, not the (huge) instruction/reloc machinery.
*
* Both tables are built once by read_orig_vmlinux(). The Elf handle stays
* open because the hashed names point into its mmapped string table.
*/
static struct {
Elf *elf;
DECLARE_HASHTABLE(syms, 16); /* name -> address */
DECLARE_HASHTABLE(symids, 16); /* .klp.symid id -> address */
} vmlinux;
/*
* Would the symbol be visible to the runtime's kallsyms-based symbol lookup?
*/
static bool vmlinux_sym_in_kallsyms(Elf *elf, GElf_Sym *sym)
{
unsigned int type = GELF_ST_TYPE(sym->st_info);
GElf_Shdr shdr;
Elf_Scn *scn;
if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE)
return false;
if (type == STT_SECTION || type == STT_FILE)
return false;
scn = elf_getscn(elf, sym->st_shndx);
if (!scn || !gelf_getshdr(scn, &shdr))
return false;
return shdr.sh_flags & SHF_ALLOC;
}
static int read_orig_vmlinux(const char *filename)
{
size_t shstrndx, nr_syms = 0, nr_symids = 0, strtab_idx = 0;
Elf_Data *symtab_data = NULL, *symid_data = NULL;
struct klp_symid *symids;
Elf_Scn *scn = NULL;
GElf_Ehdr ehdr;
int fd;
fd = open(filename, O_RDONLY);
if (fd == -1) {
ERROR_GLIBC("can't open '%s'", filename);
return -1;
}
if (elf_version(EV_CURRENT) == EV_NONE) {
ERROR_ELF("elf_version");
return -1;
}
vmlinux.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!vmlinux.elf) {
ERROR_ELF("elf_begin");
return -1;
}
if (!gelf_getehdr(vmlinux.elf, &ehdr)) {
ERROR_ELF("gelf_getehdr");
return -1;
}
if (elf_getshdrstrndx(vmlinux.elf, &shstrndx)) {
ERROR_ELF("elf_getshdrstrndx");
return -1;
}
while ((scn = elf_nextscn(vmlinux.elf, scn))) {
const char *name;
GElf_Shdr shdr;
if (!gelf_getshdr(scn, &shdr)) {
ERROR_ELF("gelf_getshdr");
return -1;
}
if (shdr.sh_type == SHT_SYMTAB) {
symtab_data = elf_getdata(scn, NULL);
if (!symtab_data) {
ERROR_ELF("elf_getdata");
return -1;
}
nr_syms = shdr.sh_size / shdr.sh_entsize;
strtab_idx = shdr.sh_link;
continue;
}
name = elf_strptr(vmlinux.elf, shstrndx, shdr.sh_name);
if (name && !strcmp(name, KLP_SYMID_SEC)) {
if (shdr.sh_size % sizeof(struct klp_symid)) {
ERROR("%s: %s: struct klp_symid size mismatch",
filename, KLP_SYMID_SEC);
return -1;
}
symid_data = elf_getdata(scn, NULL);
if (!symid_data) {
ERROR_ELF("elf_getdata");
return -1;
}
nr_symids = shdr.sh_size / sizeof(struct klp_symid);
}
}
if (!symtab_data) {
ERROR("%s: missing symbol table", filename);
return -1;
}
if (!symid_data) {
ERROR("%s: missing %s section, kernel not built with CONFIG_KLP_BUILD?",
filename, KLP_SYMID_SEC);
return -1;
}
for (size_t i = 0; i < nr_syms; i++) {
struct vmlinux_sym *vsym;
const char *name;
GElf_Sym s;
if (!gelf_getsym(symtab_data, i, &s)) {
ERROR_ELF("gelf_getsym");
return -1;
}
if (!vmlinux_sym_in_kallsyms(vmlinux.elf, &s))
continue;
name = elf_strptr(vmlinux.elf, strtab_idx, s.st_name);
if (!name)
continue;
vsym = calloc(1, sizeof(*vsym));
if (!vsym) {
ERROR_GLIBC("calloc");
return -1;
}
vsym->name = name;
vsym->addr = s.st_value;
hash_add(vmlinux.syms, &vsym->hash, str_hash(name));
}
symids = symid_data->d_buf;
for (size_t i = 0; i < nr_symids; i++) {
struct vmlinux_symid *vsymid;
vsymid = calloc(1, sizeof(*vsymid));
if (!vsymid) {
ERROR_GLIBC("calloc");
return -1;
}
vsymid->id = __bswap_if_needed(&ehdr, symids[i].id);
vsymid->addr = __bswap_if_needed(&ehdr, symids[i].addr);
hash_add(vmlinux.symids, &vsymid->hash, vsymid->id);
}
/* the fd and Elf handle stay open, the hashed names live in the mmap */
return 0;
}
/*
* Read the orig vmlinux.o's .klp.symid table, an array of entries whose 'addr'
* fields have relocs to the symbols they describe.
*/
static int read_vmlinux_o_symids(struct elf *vmlinux_o)
{
struct section *sec;
for_each_sec(vmlinux_o, sec) {
unsigned long nr;
if (strcmp(sec->name, KLP_SYMID_SEC))
continue;
if (sec_size(sec) % sizeof(struct klp_symid)) {
ERROR("%s: %s: struct klp_symid size mismatch",
vmlinux_o->name, KLP_SYMID_SEC);
return -1;
}
nr = sec_size(sec) / sizeof(struct klp_symid);
for (unsigned long i = 0; i < nr; i++) {
unsigned long offset = i * sizeof(struct klp_symid);
struct vmlinux_o_symid *entry;
struct klp_symid *symid;
struct reloc *reloc;
entry = calloc(1, sizeof(*entry));
if (!entry) {
ERROR_GLIBC("calloc");
return -1;
}
symid = sec->data->d_buf + offset;
entry->id = bswap_if_needed(vmlinux_o, symid->id);
reloc = find_reloc_by_dest(vmlinux_o, sec,
offset + offsetof(struct klp_symid, addr));
if (!reloc) {
ERROR("%s: missing reloc for %s entry",
vmlinux_o->name, KLP_SYMID_SEC);
return -1;
}
entry->sym_idx = reloc->sym->idx;
hash_add(vmlinux_o_symids, &entry->hash, entry->sym_idx);
}
}
return 0;
}
int klp_sympos_init(struct elf *orig)
{
char *filename;
int ret;
if (!str_ends_with(objname, "vmlinux.o"))
return 0;
if (read_vmlinux_o_symids(orig))
return -1;
filename = strndup(objname, strlen(objname) - 2);
if (!filename) {
ERROR_GLIBC("strndup");
return -1;
}
ret = read_orig_vmlinux(filename);
free(filename);
return ret;
}
/* Find the symbol's id in the orig vmlinux.o's .klp.symid table */
static int find_vmlinux_o_symid(struct symbol *sym, u64 *id)
{
struct vmlinux_o_symid *entry;
hash_for_each_possible(vmlinux_o_symids, entry, hash, sym->idx) {
if (entry->sym_idx == sym->idx) {
*id = entry->id;
return 0;
}
}
ERROR("no %s entry for symbol %s in orig vmlinux.o", KLP_SYMID_SEC,
sym->name);
return -1;
}
/* Find the symbol's final address in the orig vmlinux's .klp.symid table */
static int find_vmlinux_symid_addr(u64 id, u64 *addr)
{
struct vmlinux_symid *symid;
hash_for_each_possible(vmlinux.symids, symid, hash, id) {
if (symid->id == id) {
*addr = symid->addr;
return 0;
}
}
return -1;
}
/*
* Find the sympos of a vmlinux-local symbol by ranking its final address
* among the duplicately named symbols in the linked orig vmlinux, replicating
* the order in which kallsyms_on_each_match_symbol() counts them.
*/
static unsigned long find_vmlinux_sympos(struct symbol *sym)
{
unsigned long nr_matches = 0, sympos = 1;
u32 key = str_hash(sym->name);
struct vmlinux_sym *vsym;
bool found = false;
u64 id, addr;
hash_for_each_possible(vmlinux.syms, vsym, hash, key)
if (!strcmp(vsym->name, sym->name))
nr_matches++;
if (!nr_matches) {
ERROR("can't find symbol %s in orig vmlinux", sym->name);
return ULONG_MAX;
}
/*
* Unique symbols don't need disambiguating. They also have no
* .klp.symid entry, which is only emitted for names duplicated in
* vmlinux.o, so the lookups below would fail.
*/
if (nr_matches == 1)
return 0;
if (find_vmlinux_o_symid(sym, &id))
return ULONG_MAX;
if (find_vmlinux_symid_addr(id, &addr)) {
ERROR("no %s entry for symbol %s in orig vmlinux", KLP_SYMID_SEC,
sym->name);
return ULONG_MAX;
}
hash_for_each_possible(vmlinux.syms, vsym, hash, key) {
if (strcmp(vsym->name, sym->name))
continue;
if (vsym->addr < addr)
sympos++;
else if (vsym->addr == addr)
found = true;
}
if (!found) {
ERROR("%s address mismatch for symbol %s, stale orig vmlinux?",
KLP_SYMID_SEC, sym->name);
return ULONG_MAX;
}
return sympos;
}
static bool is_init_sym(struct symbol *sym)
{
return strstarts(sym->sec->name, ".init");
}
/*
* "sympos" is used by livepatch to disambiguate duplicate symbol names.
*/
unsigned long klp_find_sympos(struct elf *elf, struct symbol *sym)
{
unsigned long sympos = 0, nr_matches = 0;
bool has_dup = false;
struct symbol *s;
if (is_init_sym(sym)) {
ERROR("%s: can't patch or reference init code/data", sym->name);
return ULONG_MAX;
}
if (sym->bind != STB_LOCAL)
return 0;
/*
* vmlinux: the final link reorders symbols relative to vmlinux.o,
* so the position needs to be derived from the linked orig vmlinux via
* the .klp.symid table.
*/
if (vmlinux.elf)
return find_vmlinux_sympos(sym);
/*
* modules: the final .ko preserves symbol table order, so a
* symtab-order count here matches the runtime count done by
* module_kallsyms_on_each_symbol().
*/
for_each_sym(elf, s) {
if (!strcmp(s->name, sym->name)) {
nr_matches++;
if (s == sym)
sympos = nr_matches;
else
has_dup = true;
}
}
if (!sympos) {
ERROR("can't find sympos for %s", sym->name);
return ULONG_MAX;
}
return has_dup ? sympos : 0;
}