mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
objtool/klp: Add .klp.symid for sympos disambiguation
Livepatch identifies a duplicate-named symbol by its position (sympos) among same-named kallsyms entries, which for vmlinux are counted in ascending address order in the final linked kernel. That order can't be reliably derived from vmlinux.o: the final link reorders sub-sections (.text.unlikely*, .data..*, etc). Bridge the gap with a new .klp.symid section which can be used to correlate symbols between vmlinux.o and vmlinux so that klp-diff can reliably determine the sympos. The table can't survive --gc-sections: keeping it alive would keep every duplicate-named symbol's section alive, so the reference kernel would stop matching the one which ships. klp-build rejects CONFIG_LD_DEAD_CODE_DATA_ELIMINATION instead. Nothing is lost today: x86_64 is the only HAVE_KLP_BUILD arch and doesn't select HAVE_LD_DEAD_CODE_DATA_ELIMINATION, arm64 and s390 have never selected it either, and on powerpc, it's still EXPERIMENTAL and disabled by every distro kernel. This is the build-time half of reliable vmlinux sympos computation; "objtool klp diff" will consume the table in a subsequent commit. Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Cc: live-patching@vger.kernel.org Link: https://patch.msgid.link/64d50f077b569f47883c015cdb7079edb068efe8.1785727106.git.jpoimboe@kernel.org
This commit is contained in:
parent
f5f762fc93
commit
029223d301
|
|
@ -839,12 +839,20 @@
|
||||||
.stab.index 0 : { *(.stab.index) } \
|
.stab.index 0 : { *(.stab.index) } \
|
||||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
.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. */
|
/* Required sections not related to debugging. */
|
||||||
#define ELF_DETAILS \
|
#define ELF_DETAILS \
|
||||||
.comment 0 : { *(.comment) } \
|
.comment 0 : { *(.comment) } \
|
||||||
.symtab 0 : { *(.symtab) } \
|
.symtab 0 : { *(.symtab) } \
|
||||||
.strtab 0 : { *(.strtab) } \
|
.strtab 0 : { *(.strtab) } \
|
||||||
.shstrtab 0 : { *(.shstrtab) }
|
.shstrtab 0 : { *(.shstrtab) } \
|
||||||
|
KLP_SYMID
|
||||||
|
|
||||||
#define MODINFO \
|
#define MODINFO \
|
||||||
.modinfo : { *(.modinfo) . = ALIGN(8); }
|
.modinfo : { *(.modinfo) . = ALIGN(8); }
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,9 @@ endif
|
||||||
vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
|
vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
|
||||||
$(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
|
$(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
|
objtool-args = $(vmlinux-objtool-args-y) --link
|
||||||
|
|
||||||
# Link of vmlinux.o used for section mismatch analysis
|
# Link of vmlinux.o used for section mismatch analysis
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,9 @@ validate_config() {
|
||||||
[[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \
|
[[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \
|
||||||
die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported"
|
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 ]] && \
|
[[ -v CONFIG_AS_IS_LLVM ]] && \
|
||||||
[[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
|
[[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
|
||||||
die "Clang assembler version < 20 not supported"
|
die "Clang assembler version < 20 not supported"
|
||||||
|
|
@ -555,6 +558,8 @@ build_kernel() {
|
||||||
#
|
#
|
||||||
cmd+=("KBUILD_MODPOST_WARN=1")
|
cmd+=("KBUILD_MODPOST_WARN=1")
|
||||||
|
|
||||||
|
cmd+=("KLP_SYMIDS=1")
|
||||||
|
|
||||||
if [[ -v VERBOSE ]]; then
|
if [[ -v VERBOSE ]]; then
|
||||||
cmd+=("V=1")
|
cmd+=("V=1")
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -767,6 +767,7 @@ static const char *const section_white_list[] =
|
||||||
".llvm.call-graph-profile", /* call graph */
|
".llvm.call-graph-profile", /* call graph */
|
||||||
"__llvm_covfun",
|
"__llvm_covfun",
|
||||||
"__llvm_covmap",
|
"__llvm_covmap",
|
||||||
|
".klp.symid", /* objtool --klp-symids */
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ objtool-y += check.o
|
||||||
objtool-y += special.o
|
objtool-y += special.o
|
||||||
objtool-y += builtin-check.o
|
objtool-y += builtin-check.o
|
||||||
objtool-y += elf.o
|
objtool-y += elf.o
|
||||||
|
objtool-y += klp-symid.o
|
||||||
objtool-y += objtool.o
|
objtool-y += objtool.o
|
||||||
|
|
||||||
objtool-$(BUILD_DISAS) += disas.o
|
objtool-$(BUILD_DISAS) += disas.o
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ static const struct option check_options[] = {
|
||||||
OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
|
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_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('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('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
|
||||||
OPT_BOOLEAN(0, "noabs", &opts.noabs, "reject absolute references in allocatable sections"),
|
OPT_BOOLEAN(0, "noabs", &opts.noabs, "reject absolute references in allocatable sections"),
|
||||||
OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
|
OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
|
||||||
|
|
@ -174,10 +175,16 @@ static bool opts_valid(void)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.klp_symids && !opts.link) {
|
||||||
|
ERROR("--klp-symids requires --link");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.disas ||
|
if (opts.disas ||
|
||||||
opts.hack_jump_label ||
|
opts.hack_jump_label ||
|
||||||
opts.hack_noinstr ||
|
opts.hack_noinstr ||
|
||||||
opts.ibt ||
|
opts.ibt ||
|
||||||
|
opts.klp_symids ||
|
||||||
opts.mcount ||
|
opts.mcount ||
|
||||||
opts.noabs ||
|
opts.noabs ||
|
||||||
opts.noinstr ||
|
opts.noinstr ||
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <objtool/arch.h>
|
#include <objtool/arch.h>
|
||||||
#include <objtool/disas.h>
|
#include <objtool/disas.h>
|
||||||
#include <objtool/check.h>
|
#include <objtool/check.h>
|
||||||
|
#include <objtool/klp.h>
|
||||||
#include <objtool/special.h>
|
#include <objtool/special.h>
|
||||||
#include <objtool/trace.h>
|
#include <objtool/trace.h>
|
||||||
#include <objtool/warn.h>
|
#include <objtool/warn.h>
|
||||||
|
|
@ -4923,6 +4924,12 @@ int check(struct objtool_file *file)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.klp_symids) {
|
||||||
|
ret = klp_create_symid_sections(file);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.noabs)
|
if (opts.noabs)
|
||||||
warnings += check_abs_references(file);
|
warnings += check_abs_references(file);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ struct opts {
|
||||||
bool hack_noinstr;
|
bool hack_noinstr;
|
||||||
bool hack_skylake;
|
bool hack_skylake;
|
||||||
bool ibt;
|
bool ibt;
|
||||||
|
bool klp_symids;
|
||||||
bool mcount;
|
bool mcount;
|
||||||
bool noabs;
|
bool noabs;
|
||||||
bool noinstr;
|
bool noinstr;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,21 @@ struct klp_reloc {
|
||||||
u32 type;
|
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;
|
||||||
|
|
||||||
|
int klp_create_symid_sections(struct objtool_file *file);
|
||||||
|
|
||||||
int cmd_klp_checksum(int argc, const char **argv);
|
int cmd_klp_checksum(int argc, const char **argv);
|
||||||
int cmd_klp_diff(int argc, const char **argv);
|
int cmd_klp_diff(int argc, const char **argv);
|
||||||
int cmd_klp_post_link(int argc, const char **argv);
|
int cmd_klp_post_link(int argc, const char **argv);
|
||||||
|
|
|
||||||
117
tools/objtool/klp-symid.c
Normal file
117
tools/objtool/klp-symid.c
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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",
|
||||||
|
".modinfo",
|
||||||
|
"__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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user