Merge branch 'objtool/core' of https://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux
This series introduces new objtool features and a klp-build script to
generate livepatch modules using a source .patch as input.
This builds on concepts from the longstanding out-of-tree kpatch [1]
project which began in 2012 and has been used for many years to generate
livepatch modules for production kernels. However, this is a complete
rewrite which incorporates hard-earned lessons from 12+ years of
maintaining kpatch.
Key improvements compared to kpatch-build:
- Integrated with objtool: Leverages objtool's existing control-flow
graph analysis to help detect changed functions.
- Works on vmlinux.o: Supports late-linked objects, making it
compatible with LTO, IBT, and similar.
- Simplified code base: ~3k fewer lines of code.
- Upstream: No more out-of-tree #ifdef hacks, far less cruft.
- Cleaner internals: Vastly simplified logic for symbol/section/reloc
inclusion and special section extraction.
- Robust __LINE__ macro handling: Avoids false positive binary diffs
caused by the __LINE__ macro by introducing a fix-patch-lines script
which injects #line directives into the source .patch to preserve
the original line numbers at compile time.
The primary user interface is the klp-build script which does the
following:
- Builds an original kernel with -function-sections and
-fdata-sections, plus objtool function checksumming.
- Applies the .patch file and rebuilds the kernel using the same
options.
- Runs 'objtool klp diff' to detect changed functions and generate
intermediate binary diff objects.
- Builds a kernel module which links the diff objects with some
livepatch module init code (scripts/livepatch/init.c).
- Finalizes the livepatch module (aka work around linker wreckage)
using 'objtool klp post-link'.
I've tested with a variety of patches on defconfig and Fedora-config
kernels with both GCC and Clang.
This commit is contained in:
+2
-2
@@ -8,8 +8,8 @@ objtool-y += builtin-check.o
|
||||
objtool-y += elf.o
|
||||
objtool-y += objtool.o
|
||||
|
||||
objtool-$(BUILD_ORC) += orc_gen.o
|
||||
objtool-$(BUILD_ORC) += orc_dump.o
|
||||
objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
|
||||
objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
|
||||
|
||||
objtool-y += libstring.o
|
||||
objtool-y += libctype.o
|
||||
|
||||
+32
-16
@@ -2,6 +2,28 @@
|
||||
include ../scripts/Makefile.include
|
||||
include ../scripts/Makefile.arch
|
||||
|
||||
ifeq ($(SRCARCH),x86)
|
||||
BUILD_ORC := y
|
||||
ARCH_HAS_KLP := y
|
||||
endif
|
||||
|
||||
ifeq ($(SRCARCH),loongarch)
|
||||
BUILD_ORC := y
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH_HAS_KLP),y)
|
||||
HAVE_XXHASH = $(shell echo "int main() {}" | \
|
||||
$(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n)
|
||||
ifeq ($(HAVE_XXHASH),y)
|
||||
BUILD_KLP := y
|
||||
LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \
|
||||
-DBUILD_KLP
|
||||
LIBXXHASH_LIBS := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash)
|
||||
endif
|
||||
endif
|
||||
|
||||
export BUILD_ORC BUILD_KLP
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
@@ -23,6 +45,11 @@ LIBELF_LIBS := $(shell $(HOSTPKG_CONFIG) libelf --libs 2>/dev/null || echo -lel
|
||||
|
||||
all: $(OBJTOOL)
|
||||
|
||||
WARNINGS := -Werror -Wall -Wextra -Wmissing-prototypes \
|
||||
-Wmissing-declarations -Wwrite-strings \
|
||||
-Wno-implicit-fallthrough -Wno-sign-compare \
|
||||
-Wno-unused-parameter
|
||||
|
||||
INCLUDES := -I$(srctree)/tools/include \
|
||||
-I$(srctree)/tools/include/uapi \
|
||||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||
@@ -30,11 +57,11 @@ INCLUDES := -I$(srctree)/tools/include \
|
||||
-I$(srctree)/tools/objtool/include \
|
||||
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \
|
||||
-I$(LIBSUBCMD_OUTPUT)/include
|
||||
# Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it
|
||||
# is passed here to match a legacy behavior.
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs
|
||||
OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
||||
OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
||||
|
||||
OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS) \
|
||||
$(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS)
|
||||
|
||||
OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(HOSTLDFLAGS)
|
||||
|
||||
# Allow old libelf to be used:
|
||||
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
|
||||
@@ -46,17 +73,6 @@ HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
|
||||
AWK = awk
|
||||
MKDIR = mkdir
|
||||
|
||||
BUILD_ORC := n
|
||||
|
||||
ifeq ($(SRCARCH),x86)
|
||||
BUILD_ORC := y
|
||||
endif
|
||||
|
||||
ifeq ($(SRCARCH),loongarch)
|
||||
BUILD_ORC := y
|
||||
endif
|
||||
|
||||
export BUILD_ORC
|
||||
export srctree OUTPUT CFLAGS SRCARCH AWK
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <linux/objtool_types.h>
|
||||
#include <arch/elf.h>
|
||||
|
||||
int arch_ftrace_match(char *name)
|
||||
int arch_ftrace_match(const char *name)
|
||||
{
|
||||
return !strcmp(name, "_mcount");
|
||||
}
|
||||
@@ -17,9 +17,9 @@ unsigned long arch_jump_destination(struct instruction *insn)
|
||||
return insn->offset + (insn->immediate << 2);
|
||||
}
|
||||
|
||||
unsigned long arch_dest_reloc_offset(int addend)
|
||||
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
|
||||
{
|
||||
return addend;
|
||||
return reloc_addend(reloc);
|
||||
}
|
||||
|
||||
bool arch_pc_relative_reloc(struct reloc *reloc)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn)
|
||||
{
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int arch_ftrace_match(char *name)
|
||||
int arch_ftrace_match(const char *name)
|
||||
{
|
||||
return !strcmp(name, "_mcount");
|
||||
}
|
||||
|
||||
unsigned long arch_dest_reloc_offset(int addend)
|
||||
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
|
||||
{
|
||||
return addend;
|
||||
return reloc_addend(reloc);
|
||||
}
|
||||
|
||||
bool arch_callee_saved_reg(unsigned char reg)
|
||||
|
||||
@@ -19,11 +19,10 @@
|
||||
#include <objtool/elf.h>
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <arch/elf.h>
|
||||
|
||||
int arch_ftrace_match(char *name)
|
||||
int arch_ftrace_match(const char *name)
|
||||
{
|
||||
return !strcmp(name, "__fentry__");
|
||||
}
|
||||
@@ -68,9 +67,65 @@ bool arch_callee_saved_reg(unsigned char reg)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long arch_dest_reloc_offset(int addend)
|
||||
/* Undo the effects of __pa_symbol() if necessary */
|
||||
static unsigned long phys_to_virt(unsigned long pa)
|
||||
{
|
||||
return addend + 4;
|
||||
s64 va = pa;
|
||||
|
||||
if (va > 0)
|
||||
va &= ~(0x80000000);
|
||||
|
||||
return va;
|
||||
}
|
||||
|
||||
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
|
||||
{
|
||||
s64 addend = reloc_addend(reloc);
|
||||
|
||||
if (arch_pc_relative_reloc(reloc))
|
||||
addend += insn->offset + insn->len - reloc_offset(reloc);
|
||||
|
||||
return phys_to_virt(addend);
|
||||
}
|
||||
|
||||
static void scan_for_insn(struct section *sec, unsigned long offset,
|
||||
unsigned long *insn_off, unsigned int *insn_len)
|
||||
{
|
||||
unsigned long o = 0;
|
||||
struct insn insn;
|
||||
|
||||
while (1) {
|
||||
|
||||
insn_decode(&insn, sec->data->d_buf + o, sec_size(sec) - o,
|
||||
INSN_MODE_64);
|
||||
|
||||
if (o + insn.length > offset) {
|
||||
*insn_off = o;
|
||||
*insn_len = insn.length;
|
||||
return;
|
||||
}
|
||||
|
||||
o += insn.length;
|
||||
}
|
||||
}
|
||||
|
||||
u64 arch_adjusted_addend(struct reloc *reloc)
|
||||
{
|
||||
unsigned int type = reloc_type(reloc);
|
||||
s64 addend = reloc_addend(reloc);
|
||||
unsigned long insn_off;
|
||||
unsigned int insn_len;
|
||||
|
||||
if (type == R_X86_64_PLT32)
|
||||
return addend + 4;
|
||||
|
||||
if (type != R_X86_64_PC32 || !is_text_sec(reloc->sec->base))
|
||||
return addend;
|
||||
|
||||
scan_for_insn(reloc->sec->base, reloc_offset(reloc),
|
||||
&insn_off, &insn_len);
|
||||
|
||||
return addend + insn_off + insn_len - reloc_offset(reloc);
|
||||
}
|
||||
|
||||
unsigned long arch_jump_destination(struct instruction *insn)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn)
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
|
||||
/* look for a relocation which references .rodata */
|
||||
text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
|
||||
if (!text_reloc || !is_sec_sym(text_reloc->sym) ||
|
||||
!text_reloc->sym->sec->rodata)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -73,35 +73,38 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
|
||||
|
||||
static const struct option check_options[] = {
|
||||
OPT_GROUP("Actions:"),
|
||||
OPT_BOOLEAN(0, "checksum", &opts.checksum, "generate per-function checksums"),
|
||||
OPT_BOOLEAN(0, "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
|
||||
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('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
|
||||
OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
|
||||
OPT_BOOLEAN(0, "orc", &opts.orc, "generate ORC metadata"),
|
||||
OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
|
||||
OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
|
||||
OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
|
||||
OPT_INTEGER(0, "prefix", &opts.prefix, "generate prefix symbols"),
|
||||
OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
|
||||
OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
|
||||
OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
|
||||
OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"),
|
||||
OPT_BOOLEAN(0 , "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
|
||||
OPT_BOOLEAN(0 , "noabs", &opts.noabs, "reject absolute references in allocatable sections"),
|
||||
OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
|
||||
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
|
||||
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"),
|
||||
OPT_BOOLEAN(0, "orc", &opts.orc, "generate ORC metadata"),
|
||||
OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
|
||||
OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
|
||||
OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
|
||||
OPT_INTEGER(0, "prefix", &opts.prefix, "generate prefix symbols"),
|
||||
OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
|
||||
OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
|
||||
OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
|
||||
OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"),
|
||||
OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
|
||||
|
||||
OPT_GROUP("Options:"),
|
||||
OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
|
||||
OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
|
||||
OPT_BOOLEAN(0, "mnop", &opts.mnop, "nop out mcount call sites"),
|
||||
OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
|
||||
OPT_STRING('o', "output", &opts.output, "file", "output file name"),
|
||||
OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
|
||||
OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
|
||||
OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
|
||||
OPT_BOOLEAN(0, "Werror", &opts.werror, "return error on warnings"),
|
||||
OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
|
||||
OPT_BOOLEAN(0, "backup", &opts.backup, "create backup (.orig) file on warning/error"),
|
||||
OPT_STRING(0, "debug-checksum", &opts.debug_checksum, "funcs", "enable checksum debug output"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
|
||||
OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
|
||||
OPT_BOOLEAN(0, "mnop", &opts.mnop, "nop out mcount call sites"),
|
||||
OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
|
||||
OPT_STRING('o', "output", &opts.output, "file", "output file name"),
|
||||
OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
|
||||
OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
|
||||
OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
|
||||
OPT_BOOLEAN(0, "werror", &opts.werror, "return error on warnings"),
|
||||
|
||||
OPT_END(),
|
||||
};
|
||||
@@ -159,7 +162,20 @@ static bool opts_valid(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.hack_jump_label ||
|
||||
#ifndef BUILD_KLP
|
||||
if (opts.checksum) {
|
||||
ERROR("--checksum not supported; install xxhash-devel and recompile");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (opts.debug_checksum && !opts.checksum) {
|
||||
ERROR("--debug-checksum requires --checksum");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.checksum ||
|
||||
opts.hack_jump_label ||
|
||||
opts.hack_noinstr ||
|
||||
opts.ibt ||
|
||||
opts.mcount ||
|
||||
@@ -243,15 +259,12 @@ static void save_argv(int argc, const char **argv)
|
||||
ERROR_GLIBC("strdup(%s)", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void print_args(void)
|
||||
int make_backup(void)
|
||||
{
|
||||
char *backup = NULL;
|
||||
|
||||
if (opts.output || opts.dryrun)
|
||||
goto print;
|
||||
char *backup;
|
||||
|
||||
/*
|
||||
* Make a backup before kbuild deletes the file so the error
|
||||
@@ -260,33 +273,32 @@ void print_args(void)
|
||||
backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1);
|
||||
if (!backup) {
|
||||
ERROR_GLIBC("malloc");
|
||||
goto print;
|
||||
return 1;
|
||||
}
|
||||
|
||||
strcpy(backup, objname);
|
||||
strcat(backup, ORIG_SUFFIX);
|
||||
if (copy_file(objname, backup)) {
|
||||
backup = NULL;
|
||||
goto print;
|
||||
}
|
||||
if (copy_file(objname, backup))
|
||||
return 1;
|
||||
|
||||
print:
|
||||
/*
|
||||
* Print the cmdline args to make it easier to recreate. If '--output'
|
||||
* wasn't used, add it to the printed args with the backup as input.
|
||||
* Print the cmdline args to make it easier to recreate.
|
||||
*/
|
||||
|
||||
fprintf(stderr, "%s", orig_argv[0]);
|
||||
|
||||
for (int i = 1; i < orig_argc; i++) {
|
||||
char *arg = orig_argv[i];
|
||||
|
||||
if (backup && !strcmp(arg, objname))
|
||||
/* Modify the printed args to use the backup */
|
||||
if (!opts.output && !strcmp(arg, objname))
|
||||
fprintf(stderr, " %s -o %s", backup, objname);
|
||||
else
|
||||
fprintf(stderr, " %s", arg);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int objtool_run(int argc, const char **argv)
|
||||
@@ -332,5 +344,5 @@ int objtool_run(int argc, const char **argv)
|
||||
if (!opts.dryrun && file->elf->changed && elf_write(file->elf))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
return elf_close(file->elf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/klp.h>
|
||||
|
||||
struct subcmd {
|
||||
const char *name;
|
||||
const char *description;
|
||||
int (*fn)(int, const char **);
|
||||
};
|
||||
|
||||
static struct subcmd subcmds[] = {
|
||||
{ "diff", "Generate binary diff of two object files", cmd_klp_diff, },
|
||||
{ "post-link", "Finalize klp symbols/relocs after module linking", cmd_klp_post_link, },
|
||||
};
|
||||
|
||||
static void cmd_klp_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: objtool klp <subcommand> [<options>]\n\n");
|
||||
fprintf(stderr, "Subcommands:\n");
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(subcmds); i++) {
|
||||
struct subcmd *cmd = &subcmds[i];
|
||||
|
||||
fprintf(stderr, " %s\t%s\n", cmd->name, cmd->description);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int cmd_klp(int argc, const char **argv)
|
||||
{
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (!argc)
|
||||
cmd_klp_usage();
|
||||
|
||||
if (argc) {
|
||||
for (int i = 0; i < ARRAY_SIZE(subcmds); i++) {
|
||||
struct subcmd *cmd = &subcmds[i];
|
||||
|
||||
if (!strcmp(cmd->name, argv[0]))
|
||||
return cmd->fn(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
cmd_klp_usage();
|
||||
return 0;
|
||||
}
|
||||
+519
-356
File diff suppressed because it is too large
Load Diff
+585
-196
File diff suppressed because it is too large
Load Diff
@@ -71,7 +71,7 @@ struct stack_op {
|
||||
|
||||
struct instruction;
|
||||
|
||||
int arch_ftrace_match(char *name);
|
||||
int arch_ftrace_match(const char *name);
|
||||
|
||||
void arch_initial_func_cfi_state(struct cfi_init_state *state);
|
||||
|
||||
@@ -83,7 +83,8 @@ bool arch_callee_saved_reg(unsigned char reg);
|
||||
|
||||
unsigned long arch_jump_destination(struct instruction *insn);
|
||||
|
||||
unsigned long arch_dest_reloc_offset(int addend);
|
||||
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc);
|
||||
u64 arch_adjusted_addend(struct reloc *reloc);
|
||||
|
||||
const char *arch_nop_insn(int len);
|
||||
const char *arch_ret_insn(int len);
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
|
||||
struct opts {
|
||||
/* actions: */
|
||||
bool cfi;
|
||||
bool checksum;
|
||||
bool dump_orc;
|
||||
bool hack_jump_label;
|
||||
bool hack_noinstr;
|
||||
bool hack_skylake;
|
||||
bool ibt;
|
||||
bool mcount;
|
||||
bool noabs;
|
||||
bool noinstr;
|
||||
bool orc;
|
||||
bool retpoline;
|
||||
@@ -25,11 +28,11 @@ struct opts {
|
||||
bool static_call;
|
||||
bool uaccess;
|
||||
int prefix;
|
||||
bool cfi;
|
||||
bool noabs;
|
||||
|
||||
/* options: */
|
||||
bool backtrace;
|
||||
bool backup;
|
||||
const char *debug_checksum;
|
||||
bool dryrun;
|
||||
bool link;
|
||||
bool mnop;
|
||||
@@ -48,6 +51,8 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]);
|
||||
|
||||
int objtool_run(int argc, const char **argv);
|
||||
|
||||
void print_args(void);
|
||||
int make_backup(void);
|
||||
|
||||
int cmd_klp(int argc, const char **argv);
|
||||
|
||||
#endif /* _BUILTIN_H */
|
||||
|
||||
@@ -64,8 +64,10 @@ struct instruction {
|
||||
noendbr : 1,
|
||||
unret : 1,
|
||||
visited : 4,
|
||||
no_reloc : 1;
|
||||
/* 10 bit hole */
|
||||
no_reloc : 1,
|
||||
hole : 1,
|
||||
fake : 1;
|
||||
/* 9 bit hole */
|
||||
|
||||
struct alt_group *alt_group;
|
||||
struct instruction *jump_dest;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_CHECKSUM_H
|
||||
#define _OBJTOOL_CHECKSUM_H
|
||||
|
||||
#include <objtool/elf.h>
|
||||
|
||||
#ifdef BUILD_KLP
|
||||
|
||||
static inline void checksum_init(struct symbol *func)
|
||||
{
|
||||
if (func && !func->csum.state) {
|
||||
func->csum.state = XXH3_createState();
|
||||
XXH3_64bits_reset(func->csum.state);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void checksum_update(struct symbol *func,
|
||||
struct instruction *insn,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
XXH3_64bits_update(func->csum.state, data, size);
|
||||
dbg_checksum(func, insn, XXH3_64bits_digest(func->csum.state));
|
||||
}
|
||||
|
||||
static inline void checksum_finish(struct symbol *func)
|
||||
{
|
||||
if (func && func->csum.state) {
|
||||
func->csum.checksum = XXH3_64bits_digest(func->csum.state);
|
||||
func->csum.state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !BUILD_KLP */
|
||||
|
||||
static inline void checksum_init(struct symbol *func) {}
|
||||
static inline void checksum_update(struct symbol *func,
|
||||
struct instruction *insn,
|
||||
const void *data, size_t size) {}
|
||||
static inline void checksum_finish(struct symbol *func) {}
|
||||
|
||||
#endif /* !BUILD_KLP */
|
||||
|
||||
#endif /* _OBJTOOL_CHECKSUM_H */
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _OBJTOOL_CHECKSUM_TYPES_H
|
||||
#define _OBJTOOL_CHECKSUM_TYPES_H
|
||||
|
||||
struct sym_checksum {
|
||||
u64 addr;
|
||||
u64 checksum;
|
||||
};
|
||||
|
||||
#ifdef BUILD_KLP
|
||||
|
||||
#include <xxhash.h>
|
||||
|
||||
struct checksum {
|
||||
XXH3_state_t *state;
|
||||
XXH64_hash_t checksum;
|
||||
};
|
||||
|
||||
#else /* !BUILD_KLP */
|
||||
|
||||
struct checksum {};
|
||||
|
||||
#endif /* !BUILD_KLP */
|
||||
|
||||
#endif /* _OBJTOOL_CHECKSUM_TYPES_H */
|
||||
@@ -8,12 +8,21 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gelf.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/jhash.h>
|
||||
|
||||
#include <objtool/endianness.h>
|
||||
#include <objtool/checksum_types.h>
|
||||
#include <arch/elf.h>
|
||||
|
||||
#define SEC_NAME_LEN 1024
|
||||
#define SYM_NAME_LEN 512
|
||||
|
||||
#define bswap_if_needed(elf, val) __bswap_if_needed(&elf->ehdr, val)
|
||||
|
||||
#ifdef LIBELF_USE_DEPRECATED
|
||||
# define elf_getshdrnum elf_getshnum
|
||||
# define elf_getshdrstrndx elf_getshstrndx
|
||||
@@ -40,20 +49,23 @@ struct section {
|
||||
struct section *base, *rsec;
|
||||
struct symbol *sym;
|
||||
Elf_Data *data;
|
||||
char *name;
|
||||
const char *name;
|
||||
int idx;
|
||||
bool _changed, text, rodata, noinstr, init, truncate;
|
||||
struct reloc *relocs;
|
||||
unsigned long nr_alloc_relocs;
|
||||
struct section *twin;
|
||||
};
|
||||
|
||||
struct symbol {
|
||||
struct list_head list;
|
||||
struct list_head global_list;
|
||||
struct rb_node node;
|
||||
struct elf_hash_node hash;
|
||||
struct elf_hash_node name_hash;
|
||||
GElf_Sym sym;
|
||||
struct section *sec;
|
||||
char *name;
|
||||
const char *name, *demangled_name;
|
||||
unsigned int idx, len;
|
||||
unsigned long offset;
|
||||
unsigned long __subtree_last;
|
||||
@@ -71,9 +83,17 @@ struct symbol {
|
||||
u8 frame_pointer : 1;
|
||||
u8 ignore : 1;
|
||||
u8 nocfi : 1;
|
||||
u8 cold : 1;
|
||||
u8 prefix : 1;
|
||||
u8 debug_checksum : 1;
|
||||
u8 changed : 1;
|
||||
u8 included : 1;
|
||||
u8 klp : 1;
|
||||
struct list_head pv_target;
|
||||
struct reloc *relocs;
|
||||
struct section *group_sec;
|
||||
struct checksum csum;
|
||||
struct symbol *twin, *clone;
|
||||
};
|
||||
|
||||
struct reloc {
|
||||
@@ -88,9 +108,10 @@ struct elf {
|
||||
GElf_Ehdr ehdr;
|
||||
int fd;
|
||||
bool changed;
|
||||
char *name;
|
||||
const char *name, *tmp_name;
|
||||
unsigned int num_files;
|
||||
struct list_head sections;
|
||||
struct list_head symbols;
|
||||
unsigned long num_relocs;
|
||||
|
||||
int symbol_bits;
|
||||
@@ -110,14 +131,37 @@ struct elf {
|
||||
};
|
||||
|
||||
struct elf *elf_open_read(const char *name, int flags);
|
||||
struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name);
|
||||
|
||||
struct section *elf_create_section(struct elf *elf, const char *name,
|
||||
size_t entsize, unsigned int nr);
|
||||
size_t size, size_t entsize,
|
||||
unsigned int type, unsigned int align,
|
||||
unsigned int flags);
|
||||
struct section *elf_create_section_pair(struct elf *elf, const char *name,
|
||||
size_t entsize, unsigned int nr,
|
||||
unsigned int reloc_nr);
|
||||
|
||||
struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
|
||||
struct section *elf_create_rela_section(struct elf *elf, struct section *sec,
|
||||
unsigned int reloc_nr);
|
||||
|
||||
struct symbol *elf_create_symbol(struct elf *elf, const char *name,
|
||||
struct section *sec, unsigned int bind,
|
||||
unsigned int type, unsigned long offset,
|
||||
size_t size);
|
||||
struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec);
|
||||
|
||||
void *elf_add_data(struct elf *elf, struct section *sec, const void *data,
|
||||
size_t size);
|
||||
|
||||
unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char *str);
|
||||
|
||||
struct reloc *elf_create_reloc(struct elf *elf, struct section *sec,
|
||||
unsigned long offset, struct symbol *sym,
|
||||
s64 addend, unsigned int type);
|
||||
|
||||
struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
|
||||
unsigned int reloc_idx, unsigned long offset,
|
||||
struct symbol *sym, s64 addend, unsigned int type);
|
||||
|
||||
struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
|
||||
unsigned long offset,
|
||||
@@ -131,16 +175,17 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
|
||||
struct symbol *sym,
|
||||
s64 addend);
|
||||
|
||||
int elf_write_insn(struct elf *elf, struct section *sec,
|
||||
unsigned long offset, unsigned int len,
|
||||
const char *insn);
|
||||
int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
|
||||
unsigned int len, const char *insn);
|
||||
|
||||
int elf_write(struct elf *elf);
|
||||
void elf_close(struct elf *elf);
|
||||
int elf_close(struct elf *elf);
|
||||
|
||||
struct section *find_section_by_name(const struct elf *elf, const char *name);
|
||||
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
|
||||
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
|
||||
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
|
||||
struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name);
|
||||
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
|
||||
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
|
||||
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
|
||||
@@ -178,11 +223,76 @@ static inline unsigned int elf_text_rela_type(struct elf *elf)
|
||||
return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
|
||||
}
|
||||
|
||||
static inline bool is_undef_sym(struct symbol *sym)
|
||||
{
|
||||
return !sym->sec->idx;
|
||||
}
|
||||
|
||||
static inline bool is_null_sym(struct symbol *sym)
|
||||
{
|
||||
return !sym->idx;
|
||||
}
|
||||
|
||||
static inline bool is_sec_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->type == STT_SECTION;
|
||||
}
|
||||
|
||||
static inline bool is_object_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->type == STT_OBJECT;
|
||||
}
|
||||
|
||||
static inline bool is_func_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->type == STT_FUNC;
|
||||
}
|
||||
|
||||
static inline bool is_file_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->type == STT_FILE;
|
||||
}
|
||||
|
||||
static inline bool is_notype_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->type == STT_NOTYPE;
|
||||
}
|
||||
|
||||
static inline bool is_global_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->bind == STB_GLOBAL;
|
||||
}
|
||||
|
||||
static inline bool is_weak_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->bind == STB_WEAK;
|
||||
}
|
||||
|
||||
static inline bool is_local_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->bind == STB_LOCAL;
|
||||
}
|
||||
|
||||
static inline bool is_prefix_func(struct symbol *sym)
|
||||
{
|
||||
return sym->prefix;
|
||||
}
|
||||
|
||||
static inline bool is_reloc_sec(struct section *sec)
|
||||
{
|
||||
return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
|
||||
}
|
||||
|
||||
static inline bool is_string_sec(struct section *sec)
|
||||
{
|
||||
return sec->sh.sh_flags & SHF_STRINGS;
|
||||
}
|
||||
|
||||
static inline bool is_text_sec(struct section *sec)
|
||||
{
|
||||
return sec->sh.sh_flags & SHF_EXECINSTR;
|
||||
}
|
||||
|
||||
static inline bool sec_changed(struct section *sec)
|
||||
{
|
||||
return sec->_changed;
|
||||
@@ -223,6 +333,11 @@ static inline bool is_32bit_reloc(struct reloc *reloc)
|
||||
return reloc->sec->sh.sh_entsize < 16;
|
||||
}
|
||||
|
||||
static inline unsigned long sec_size(struct section *sec)
|
||||
{
|
||||
return sec->sh.sh_size;
|
||||
}
|
||||
|
||||
#define __get_reloc_field(reloc, field) \
|
||||
({ \
|
||||
is_32bit_reloc(reloc) ? \
|
||||
@@ -300,6 +415,15 @@ static inline void set_reloc_type(struct elf *elf, struct reloc *reloc, unsigned
|
||||
mark_sec_changed(elf, reloc->sec, true);
|
||||
}
|
||||
|
||||
static inline unsigned int annotype(struct elf *elf, struct section *sec,
|
||||
struct reloc *reloc)
|
||||
{
|
||||
unsigned int type;
|
||||
|
||||
type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * 8) + 4);
|
||||
return bswap_if_needed(elf, type);
|
||||
}
|
||||
|
||||
#define RELOC_JUMP_TABLE_BIT 1UL
|
||||
|
||||
/* Does reloc mark the beginning of a jump table? */
|
||||
@@ -325,28 +449,54 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
|
||||
reloc->_sym_next_reloc = (unsigned long)next | bit;
|
||||
}
|
||||
|
||||
#define for_each_sec(file, sec) \
|
||||
list_for_each_entry(sec, &file->elf->sections, list)
|
||||
#define for_each_sec(elf, sec) \
|
||||
list_for_each_entry(sec, &elf->sections, list)
|
||||
|
||||
#define sec_for_each_sym(sec, sym) \
|
||||
list_for_each_entry(sym, &sec->symbol_list, list)
|
||||
|
||||
#define for_each_sym(file, sym) \
|
||||
for (struct section *__sec, *__fake = (struct section *)1; \
|
||||
__fake; __fake = NULL) \
|
||||
for_each_sec(file, __sec) \
|
||||
sec_for_each_sym(__sec, sym)
|
||||
#define sec_prev_sym(sym) \
|
||||
sym->sec && sym->list.prev != &sym->sec->symbol_list ? \
|
||||
list_prev_entry(sym, list) : NULL
|
||||
|
||||
#define for_each_sym(elf, sym) \
|
||||
list_for_each_entry(sym, &elf->symbols, global_list)
|
||||
|
||||
#define for_each_sym_continue(elf, sym) \
|
||||
list_for_each_entry_continue(sym, &elf->symbols, global_list)
|
||||
|
||||
#define rsec_next_reloc(rsec, reloc) \
|
||||
reloc_idx(reloc) < sec_num_entries(rsec) - 1 ? reloc + 1 : NULL
|
||||
|
||||
#define for_each_reloc(rsec, reloc) \
|
||||
for (int __i = 0, __fake = 1; __fake; __fake = 0) \
|
||||
for (reloc = rsec->relocs; \
|
||||
__i < sec_num_entries(rsec); \
|
||||
__i++, reloc++)
|
||||
for (reloc = rsec->relocs; reloc; reloc = rsec_next_reloc(rsec, reloc))
|
||||
|
||||
#define for_each_reloc_from(rsec, reloc) \
|
||||
for (int __i = reloc_idx(reloc); \
|
||||
__i < sec_num_entries(rsec); \
|
||||
__i++, reloc++)
|
||||
for (; reloc; reloc = rsec_next_reloc(rsec, reloc))
|
||||
|
||||
#define for_each_reloc_continue(rsec, reloc) \
|
||||
for (reloc = rsec_next_reloc(rsec, reloc); reloc; \
|
||||
reloc = rsec_next_reloc(rsec, reloc))
|
||||
|
||||
#define sym_for_each_reloc(elf, sym, reloc) \
|
||||
for (reloc = find_reloc_by_dest_range(elf, sym->sec, \
|
||||
sym->offset, sym->len); \
|
||||
reloc && reloc_offset(reloc) < sym->offset + sym->len; \
|
||||
reloc = rsec_next_reloc(sym->sec->rsec, reloc))
|
||||
|
||||
static inline struct symbol *get_func_prefix(struct symbol *func)
|
||||
{
|
||||
struct symbol *prev;
|
||||
|
||||
if (!is_func_sym(func))
|
||||
return NULL;
|
||||
|
||||
prev = sec_prev_sym(func);
|
||||
if (prev && is_prefix_func(prev))
|
||||
return prev;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define OFFSET_STRIDE_BITS 4
|
||||
#define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <endian.h>
|
||||
#include <objtool/elf.h>
|
||||
|
||||
/*
|
||||
* Does a byte swap if target file endianness doesn't match the host, i.e. cross
|
||||
@@ -12,16 +11,16 @@
|
||||
* To be used for multi-byte values conversion, which are read from / about
|
||||
* to be written to a target native endianness ELF file.
|
||||
*/
|
||||
static inline bool need_bswap(struct elf *elf)
|
||||
static inline bool need_bswap(GElf_Ehdr *ehdr)
|
||||
{
|
||||
return (__BYTE_ORDER == __LITTLE_ENDIAN) ^
|
||||
(elf->ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
(ehdr->e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
}
|
||||
|
||||
#define bswap_if_needed(elf, val) \
|
||||
#define __bswap_if_needed(ehdr, val) \
|
||||
({ \
|
||||
__typeof__(val) __ret; \
|
||||
bool __need_bswap = need_bswap(elf); \
|
||||
bool __need_bswap = need_bswap(ehdr); \
|
||||
switch (sizeof(val)) { \
|
||||
case 8: \
|
||||
__ret = __need_bswap ? bswap_64(val) : (val); break; \
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_KLP_H
|
||||
#define _OBJTOOL_KLP_H
|
||||
|
||||
#define SHF_RELA_LIVEPATCH 0x00100000
|
||||
#define SHN_LIVEPATCH 0xff20
|
||||
|
||||
/*
|
||||
* __klp_objects and __klp_funcs are created by klp diff and used by the patch
|
||||
* module init code to build the klp_patch, klp_object and klp_func structs
|
||||
* needed by the livepatch API.
|
||||
*/
|
||||
#define KLP_OBJECTS_SEC "__klp_objects"
|
||||
#define KLP_FUNCS_SEC "__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
|
||||
* SHF_RELA_LIVEPATCH, nor does it support having two RELA sections for a
|
||||
* single PROGBITS section.
|
||||
*/
|
||||
#define KLP_RELOCS_SEC "__klp_relocs"
|
||||
#define KLP_STRINGS_SEC ".rodata.klp.str1.1"
|
||||
|
||||
struct klp_reloc {
|
||||
void *offset;
|
||||
void *sym;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
int cmd_klp_diff(int argc, const char **argv);
|
||||
int cmd_klp_post_link(int argc, const char **argv);
|
||||
|
||||
#endif /* _OBJTOOL_KLP_H */
|
||||
@@ -28,7 +28,7 @@ struct objtool_file {
|
||||
struct list_head mcount_loc_list;
|
||||
struct list_head endbr_list;
|
||||
struct list_head call_list;
|
||||
bool ignore_unreachables, hints, rodata;
|
||||
bool ignore_unreachables, hints, rodata, klp;
|
||||
|
||||
unsigned int nr_endbr;
|
||||
unsigned int nr_endbr_int;
|
||||
@@ -39,6 +39,8 @@ struct objtool_file {
|
||||
struct pv_state *pv_ops;
|
||||
};
|
||||
|
||||
char *top_level_dir(const char *file);
|
||||
|
||||
struct objtool_file *objtool_open_read(const char *_objname);
|
||||
|
||||
int objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
#include <objtool/warn.h>
|
||||
|
||||
#define snprintf_check(str, size, format, args...) \
|
||||
({ \
|
||||
int __ret = snprintf(str, size, format, args); \
|
||||
if (__ret < 0) \
|
||||
ERROR_GLIBC("snprintf"); \
|
||||
else if (__ret >= size) \
|
||||
ERROR("snprintf() failed for '" format "'", args); \
|
||||
else \
|
||||
__ret = 0; \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#endif /* _UTIL_H */
|
||||
@@ -102,4 +102,44 @@ static inline char *offstr(struct section *sec, unsigned long offset)
|
||||
#define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)
|
||||
#define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
|
||||
|
||||
extern bool debug;
|
||||
extern int indent;
|
||||
|
||||
static inline void unindent(int *unused) { indent--; }
|
||||
|
||||
#define __dbg(format, ...) \
|
||||
fprintf(stderr, \
|
||||
"DEBUG: %s%s" format "\n", \
|
||||
objname ?: "", \
|
||||
objname ? ": " : "", \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define dbg(args...) \
|
||||
({ \
|
||||
if (unlikely(debug)) \
|
||||
__dbg(args); \
|
||||
})
|
||||
|
||||
#define __dbg_indent(format, ...) \
|
||||
({ \
|
||||
if (unlikely(debug)) \
|
||||
__dbg("%*s" format, indent * 8, "", ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define dbg_indent(args...) \
|
||||
int __attribute__((cleanup(unindent))) __dummy_##__COUNTER__; \
|
||||
__dbg_indent(args); \
|
||||
indent++
|
||||
|
||||
#define dbg_checksum(func, insn, checksum) \
|
||||
({ \
|
||||
if (unlikely(insn->sym && insn->sym->pfunc && \
|
||||
insn->sym->pfunc->debug_checksum)) { \
|
||||
char *insn_off = offstr(insn->sec, insn->offset); \
|
||||
__dbg("checksum: %s %s %016lx", \
|
||||
func->name, insn_off, checksum); \
|
||||
free(insn_off); \
|
||||
} \
|
||||
})
|
||||
|
||||
#endif /* _WARN_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Read the intermediate KLP reloc/symbol representations created by klp diff
|
||||
* and convert them to the proper format required 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 like
|
||||
* SHN_LIVEPATCH.
|
||||
*
|
||||
* This is the final tool in the livepatch module generation pipeline:
|
||||
*
|
||||
* kernel builds -> objtool klp diff -> module link -> objtool klp post-link
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/klp.h>
|
||||
#include <objtool/util.h>
|
||||
#include <linux/livepatch_external.h>
|
||||
|
||||
static int fix_klp_relocs(struct elf *elf)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sec_size(klp_relocs) / sizeof(struct klp_reloc); i++) {
|
||||
struct klp_reloc *klp_reloc;
|
||||
unsigned long klp_reloc_off;
|
||||
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;
|
||||
|
||||
klp_reloc_off = i * sizeof(*klp_reloc);
|
||||
klp_reloc = klp_relocs->data->d_buf + klp_reloc_off;
|
||||
|
||||
/*
|
||||
* Read __klp_relocs[i]:
|
||||
*/
|
||||
|
||||
/* klp_reloc.sec_offset */
|
||||
reloc = find_reloc_by_dest(elf, klp_relocs,
|
||||
klp_reloc_off + offsetof(struct klp_reloc, offset));
|
||||
if (!reloc) {
|
||||
ERROR("malformed " KLP_RELOCS_SEC " section");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec = reloc->sym->sec;
|
||||
offset = reloc_addend(reloc);
|
||||
|
||||
/* klp_reloc.sym */
|
||||
reloc = find_reloc_by_dest(elf, klp_relocs,
|
||||
klp_reloc_off + offsetof(struct klp_reloc, sym));
|
||||
if (!reloc) {
|
||||
ERROR("malformed " KLP_RELOCS_SEC " section");
|
||||
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:
|
||||
*/
|
||||
|
||||
/* 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))
|
||||
return -1;
|
||||
|
||||
klp_rsec = find_section_by_name(elf, rsec_name);
|
||||
if (!klp_rsec) {
|
||||
klp_rsec = elf_create_section(elf, rsec_name, 0,
|
||||
elf_rela_size(elf),
|
||||
SHT_RELA, elf_addr_size(elf),
|
||||
SHF_ALLOC | SHF_INFO_LINK | SHF_RELA_LIVEPATCH);
|
||||
if (!klp_rsec)
|
||||
return -1;
|
||||
|
||||
klp_rsec->sh.sh_link = symtab->idx;
|
||||
klp_rsec->sh.sh_info = sec->idx;
|
||||
klp_rsec->base = sec;
|
||||
}
|
||||
|
||||
tmp = sec->rsec;
|
||||
sec->rsec = klp_rsec;
|
||||
if (!elf_create_reloc(elf, sec, offset, klp_sym, addend, klp_reloc->type))
|
||||
return -1;
|
||||
sec->rsec = tmp;
|
||||
|
||||
/*
|
||||
* Fix up the corresponding KLP symbol:
|
||||
*/
|
||||
|
||||
klp_sym->sym.st_shndx = SHN_LIVEPATCH;
|
||||
if (!gelf_update_sym(symtab->data, klp_sym->idx, &klp_sym->sym)) {
|
||||
ERROR_ELF("gelf_update_sym");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the original non-KLP reloc by converting it to R_*_NONE:
|
||||
*/
|
||||
|
||||
reloc = find_reloc_by_dest(elf, sec, offset);
|
||||
sym = reloc->sym;
|
||||
sym->sym.st_shndx = SHN_LIVEPATCH;
|
||||
set_reloc_type(elf, reloc, 0);
|
||||
if (!gelf_update_sym(symtab->data, sym->idx, &sym->sym)) {
|
||||
ERROR_ELF("gelf_update_sym");
|
||||
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.
|
||||
* Linkers don't tend to handle the "two rela sections for a single base
|
||||
* section" case very well, nor do they appreciate SHN_LIVEPATCH.
|
||||
*/
|
||||
int cmd_klp_post_link(int argc, const char **argv)
|
||||
{
|
||||
struct elf *elf;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (argc != 1) {
|
||||
fprintf(stderr, "%d\n", argc);
|
||||
fprintf(stderr, "usage: objtool link <file.ko>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
elf = elf_open_read(argv[0], O_RDWR);
|
||||
if (!elf)
|
||||
return -1;
|
||||
|
||||
if (fix_klp_relocs(elf))
|
||||
return -1;
|
||||
|
||||
if (elf_write(elf))
|
||||
return -1;
|
||||
|
||||
return elf_close(elf);
|
||||
}
|
||||
+41
-1
@@ -16,7 +16,8 @@
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
|
||||
bool help;
|
||||
bool debug;
|
||||
int indent;
|
||||
|
||||
static struct objtool_file file;
|
||||
|
||||
@@ -71,6 +72,39 @@ int objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *top_level_dir(const char *file)
|
||||
{
|
||||
ssize_t len, self_len, file_len;
|
||||
char self[PATH_MAX], *str;
|
||||
int i;
|
||||
|
||||
len = readlink("/proc/self/exe", self, sizeof(self) - 1);
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
self[len] = '\0';
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
char *s = strrchr(self, '/');
|
||||
if (!s)
|
||||
return NULL;
|
||||
*s = '\0';
|
||||
}
|
||||
|
||||
self_len = strlen(self);
|
||||
file_len = strlen(file);
|
||||
|
||||
str = malloc(self_len + file_len + 2);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
memcpy(str, self, self_len);
|
||||
str[self_len] = '/';
|
||||
strcpy(str + self_len + 1, file);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED";
|
||||
@@ -79,5 +113,11 @@ int main(int argc, const char **argv)
|
||||
exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED);
|
||||
pager_init(UNUSED);
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "klp")) {
|
||||
argc--;
|
||||
argv++;
|
||||
return cmd_klp(argc, argv);
|
||||
}
|
||||
|
||||
return objtool_run(argc, argv);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int orc_dump(const char *filename)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
struct orc_list_entry {
|
||||
struct list_head list;
|
||||
@@ -57,7 +56,7 @@ int orc_create(struct objtool_file *file)
|
||||
|
||||
/* Build a deduplicated list of ORC entries: */
|
||||
INIT_LIST_HEAD(&orc_list);
|
||||
for_each_sec(file, sec) {
|
||||
for_each_sec(file->elf, sec) {
|
||||
struct orc_entry orc, prev_orc = {0};
|
||||
struct instruction *insn;
|
||||
bool empty = true;
|
||||
@@ -127,7 +126,11 @@ int orc_create(struct objtool_file *file)
|
||||
return -1;
|
||||
}
|
||||
orc_sec = elf_create_section(file->elf, ".orc_unwind",
|
||||
sizeof(struct orc_entry), nr);
|
||||
nr * sizeof(struct orc_entry),
|
||||
sizeof(struct orc_entry),
|
||||
SHT_PROGBITS,
|
||||
1,
|
||||
SHF_ALLOC);
|
||||
if (!orc_sec)
|
||||
return -1;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/special.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
struct special_entry {
|
||||
const char *sec;
|
||||
@@ -133,7 +132,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
|
||||
struct section *sec;
|
||||
unsigned int nr_entries;
|
||||
struct special_alt *alt;
|
||||
int idx, ret;
|
||||
int idx;
|
||||
|
||||
INIT_LIST_HEAD(alts);
|
||||
|
||||
@@ -142,12 +141,12 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
|
||||
if (!sec)
|
||||
continue;
|
||||
|
||||
if (sec->sh.sh_size % entry->size != 0) {
|
||||
if (sec_size(sec) % entry->size != 0) {
|
||||
ERROR("%s size not a multiple of %d", sec->name, entry->size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nr_entries = sec->sh.sh_size / entry->size;
|
||||
nr_entries = sec_size(sec) / entry->size;
|
||||
|
||||
for (idx = 0; idx < nr_entries; idx++) {
|
||||
alt = malloc(sizeof(*alt));
|
||||
@@ -157,11 +156,8 @@ int special_get_alts(struct elf *elf, struct list_head *alts)
|
||||
}
|
||||
memset(alt, 0, sizeof(*alt));
|
||||
|
||||
ret = get_alt_entry(elf, entry, sec, idx, alt);
|
||||
if (ret > 0)
|
||||
continue;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (get_alt_entry(elf, entry, sec, idx, alt))
|
||||
return -1;
|
||||
|
||||
list_add_tail(&alt->list, alts);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ arch/x86/include/asm/orc_types.h
|
||||
arch/x86/include/asm/emulate_prefix.h
|
||||
arch/x86/lib/x86-opcode-map.txt
|
||||
arch/x86/tools/gen-insn-attr-x86.awk
|
||||
include/linux/interval_tree_generic.h
|
||||
include/linux/livepatch_external.h
|
||||
include/linux/static_call_types.h
|
||||
"
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/builtin.h>
|
||||
|
||||
#define UNSUPPORTED(name) \
|
||||
({ \
|
||||
@@ -24,3 +26,8 @@ int __weak orc_create(struct objtool_file *file)
|
||||
{
|
||||
UNSUPPORTED("ORC");
|
||||
}
|
||||
|
||||
int __weak cmd_klp(int argc, const char **argv)
|
||||
{
|
||||
UNSUPPORTED("klp");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user