objtool/klp: Introduce klp diff subcommand for diffing object files

Add a new klp diff subcommand which performs a binary diff between two
object files and extracts changed functions into a new object which can
then be linked into a livepatch module.

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
    (coming in a later patch) which injects #line directives into the
    source .patch to preserve the original line numbers at compile time.

Note the end result of this subcommand is not yet functionally complete.
Livepatch needs some ELF magic which linkers don't like:

  - Two relocation sections (.rela*, .klp.rela*) for the same text
    section.

  - Use of SHN_LIVEPATCH to mark livepatch symbols.

Unfortunately linkers tend to mangle such things.  To work around that,
klp diff generates a linker-compliant intermediate binary which encodes
the relevant KLP section/reloc/symbol metadata.

After module linking, a klp post-link step (coming soon) will clean up
the mess and convert the linked .ko into a fully compliant livepatch
module.

Note this subcommand requires the diffed binaries to have been compiled
with -ffunction-sections and -fdata-sections, and processed with
'objtool --checksum'.  Those constraints will be handled by a klp-build
script introduced in a later patch.

Without '-ffunction-sections -fdata-sections', reliable object diffing
would be infeasible due to toolchain limitations:

  - For intra-file+intra-section references, the compiler might
    occasionally generated hard-coded instruction offsets instead of
    relocations.

  - Section-symbol-based references can be ambiguous:

    - Overlapping or zero-length symbols create ambiguity as to which
      symbol is being referenced.

    - A reference to the end of a symbol (e.g., checking array bounds)
      can be misinterpreted as a reference to the next symbol, or vice
      versa.

A potential future alternative to '-ffunction-sections -fdata-sections'
would be to introduce a toolchain option that forces symbol-based
(non-section) relocations.

Acked-by: Petr Mladek <pmladek@suse.com>
Tested-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
This commit is contained in:
Josh Poimboeuf 2025-09-17 09:03:59 -07:00
parent a3493b3338
commit dd590d4d57
23 changed files with 2088 additions and 59 deletions

View File

@ -14439,7 +14439,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.g
F: Documentation/ABI/testing/sysfs-kernel-livepatch
F: Documentation/livepatch/
F: arch/powerpc/include/asm/livepatch.h
F: include/linux/livepatch.h
F: include/linux/livepatch*.h
F: kernel/livepatch/
F: kernel/module/livepatch.c
F: samples/livepatch/

View File

@ -13,6 +13,7 @@
#include <linux/ftrace.h>
#include <linux/completion.h>
#include <linux/list.h>
#include <linux/livepatch_external.h>
#include <linux/livepatch_sched.h>
#if IS_ENABLED(CONFIG_LIVEPATCH)
@ -77,30 +78,6 @@ struct klp_func {
bool transition;
};
struct klp_object;
/**
* struct klp_callbacks - pre/post live-(un)patch callback structure
* @pre_patch: executed before code patching
* @post_patch: executed after code patching
* @pre_unpatch: executed before code unpatching
* @post_unpatch: executed after code unpatching
* @post_unpatch_enabled: flag indicating if post-unpatch callback
* should run
*
* All callbacks are optional. Only the pre-patch callback, if provided,
* will be unconditionally executed. If the parent klp_object fails to
* patch for any reason, including a non-zero error status returned from
* the pre-patch callback, no further callbacks will be executed.
*/
struct klp_callbacks {
int (*pre_patch)(struct klp_object *obj);
void (*post_patch)(struct klp_object *obj);
void (*pre_unpatch)(struct klp_object *obj);
void (*post_unpatch)(struct klp_object *obj);
bool post_unpatch_enabled;
};
/**
* struct klp_object - kernel object structure for live patching
* @name: module name (or NULL for vmlinux)

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* External livepatch interfaces for patch creation tooling
*/
#ifndef _LINUX_LIVEPATCH_EXTERNAL_H_
#define _LINUX_LIVEPATCH_EXTERNAL_H_
#include <linux/types.h>
#define KLP_RELOC_SEC_PREFIX ".klp.rela."
#define KLP_SYM_PREFIX ".klp.sym."
#define __KLP_PRE_PATCH_PREFIX __klp_pre_patch_callback_
#define __KLP_POST_PATCH_PREFIX __klp_post_patch_callback_
#define __KLP_PRE_UNPATCH_PREFIX __klp_pre_unpatch_callback_
#define __KLP_POST_UNPATCH_PREFIX __klp_post_unpatch_callback_
#define KLP_PRE_PATCH_PREFIX __stringify(__KLP_PRE_PATCH_PREFIX)
#define KLP_POST_PATCH_PREFIX __stringify(__KLP_POST_PATCH_PREFIX)
#define KLP_PRE_UNPATCH_PREFIX __stringify(__KLP_PRE_UNPATCH_PREFIX)
#define KLP_POST_UNPATCH_PREFIX __stringify(__KLP_POST_UNPATCH_PREFIX)
struct klp_object;
typedef int (*klp_pre_patch_t)(struct klp_object *obj);
typedef void (*klp_post_patch_t)(struct klp_object *obj);
typedef void (*klp_pre_unpatch_t)(struct klp_object *obj);
typedef void (*klp_post_unpatch_t)(struct klp_object *obj);
/**
* struct klp_callbacks - pre/post live-(un)patch callback structure
* @pre_patch: executed before code patching
* @post_patch: executed after code patching
* @pre_unpatch: executed before code unpatching
* @post_unpatch: executed after code unpatching
* @post_unpatch_enabled: flag indicating if post-unpatch callback
* should run
*
* All callbacks are optional. Only the pre-patch callback, if provided,
* will be unconditionally executed. If the parent klp_object fails to
* patch for any reason, including a non-zero error status returned from
* the pre-patch callback, no further callbacks will be executed.
*/
struct klp_callbacks {
klp_pre_patch_t pre_patch;
klp_post_patch_t post_patch;
klp_pre_unpatch_t pre_unpatch;
klp_post_unpatch_t post_unpatch;
bool post_unpatch_enabled;
};
/*
* 'struct klp_{func,object}_ext' are compact "external" representations of
* 'struct klp_{func,object}'. They are used by objtool for livepatch
* generation. The structs are then read by the livepatch module and converted
* to the real structs before calling klp_enable_patch().
*
* TODO make these the official API for klp_enable_patch(). That should
* simplify livepatch's interface as well as its data structure lifetime
* management.
*/
struct klp_func_ext {
const char *old_name;
void *new_func;
unsigned long sympos;
};
struct klp_object_ext {
const char *name;
struct klp_func_ext *funcs;
struct klp_callbacks callbacks;
unsigned int nr_funcs;
};
#endif /* _LINUX_LIVEPATCH_EXTERNAL_H_ */

View File

@ -224,7 +224,7 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
/* Format: .klp.sym.sym_objname.sym_name,sympos */
cnt = sscanf(strtab + sym->st_name,
".klp.sym.%55[^.].%511[^,],%lu",
KLP_SYM_PREFIX "%55[^.].%511[^,],%lu",
sym_objname, sym_name, &sympos);
if (cnt != 3) {
pr_err("symbol %s has an incorrectly formatted name\n",
@ -303,7 +303,7 @@ static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
* See comment in klp_resolve_symbols() for an explanation
* of the selected field width value.
*/
cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]",
cnt = sscanf(shstrtab + sec->sh_name, KLP_RELOC_SEC_PREFIX "%55[^.]",
sec_objname);
if (cnt != 1) {
pr_err("section %s has an incorrectly formatted name\n",

View File

@ -34,8 +34,16 @@ SECTIONS {
__patchable_function_entries : { *(__patchable_function_entries) }
__klp_funcs 0: ALIGN(8) { KEEP(*(__klp_funcs)) }
__klp_objects 0: ALIGN(8) {
__start_klp_objects = .;
KEEP(*(__klp_objects))
__stop_klp_objects = .;
}
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
__kcfi_traps : { KEEP(*(.kcfi_traps)) }
__kcfi_traps : { KEEP(*(.kcfi_traps)) }
#endif
.text : {

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* External livepatch interfaces for patch creation tooling
*/
#ifndef _LINUX_LIVEPATCH_EXTERNAL_H_
#define _LINUX_LIVEPATCH_EXTERNAL_H_
#include <linux/types.h>
#define KLP_RELOC_SEC_PREFIX ".klp.rela."
#define KLP_SYM_PREFIX ".klp.sym."
#define __KLP_PRE_PATCH_PREFIX __klp_pre_patch_callback_
#define __KLP_POST_PATCH_PREFIX __klp_post_patch_callback_
#define __KLP_PRE_UNPATCH_PREFIX __klp_pre_unpatch_callback_
#define __KLP_POST_UNPATCH_PREFIX __klp_post_unpatch_callback_
#define KLP_PRE_PATCH_PREFIX __stringify(__KLP_PRE_PATCH_PREFIX)
#define KLP_POST_PATCH_PREFIX __stringify(__KLP_POST_PATCH_PREFIX)
#define KLP_PRE_UNPATCH_PREFIX __stringify(__KLP_PRE_UNPATCH_PREFIX)
#define KLP_POST_UNPATCH_PREFIX __stringify(__KLP_POST_UNPATCH_PREFIX)
struct klp_object;
typedef int (*klp_pre_patch_t)(struct klp_object *obj);
typedef void (*klp_post_patch_t)(struct klp_object *obj);
typedef void (*klp_pre_unpatch_t)(struct klp_object *obj);
typedef void (*klp_post_unpatch_t)(struct klp_object *obj);
/**
* struct klp_callbacks - pre/post live-(un)patch callback structure
* @pre_patch: executed before code patching
* @post_patch: executed after code patching
* @pre_unpatch: executed before code unpatching
* @post_unpatch: executed after code unpatching
* @post_unpatch_enabled: flag indicating if post-unpatch callback
* should run
*
* All callbacks are optional. Only the pre-patch callback, if provided,
* will be unconditionally executed. If the parent klp_object fails to
* patch for any reason, including a non-zero error status returned from
* the pre-patch callback, no further callbacks will be executed.
*/
struct klp_callbacks {
klp_pre_patch_t pre_patch;
klp_post_patch_t post_patch;
klp_pre_unpatch_t pre_unpatch;
klp_post_unpatch_t post_unpatch;
bool post_unpatch_enabled;
};
/*
* 'struct klp_{func,object}_ext' are compact "external" representations of
* 'struct klp_{func,object}'. They are used by objtool for livepatch
* generation. The structs are then read by the livepatch module and converted
* to the real structs before calling klp_enable_patch().
*
* TODO make these the official API for klp_enable_patch(). That should
* simplify livepatch's interface as well as its data structure lifetime
* management.
*/
struct klp_func_ext {
const char *old_name;
void *new_func;
unsigned long sympos;
};
struct klp_object_ext {
const char *name;
struct klp_func_ext *funcs;
struct klp_callbacks callbacks;
unsigned int nr_funcs;
};
#endif /* _LINUX_LIVEPATCH_EXTERNAL_H_ */

View File

@ -44,6 +44,20 @@ static inline bool strstarts(const char *str, const char *prefix)
return strncmp(str, prefix, strlen(prefix)) == 0;
}
/*
* Checks if a string ends with another.
*/
static inline bool str_ends_with(const char *str, const char *substr)
{
size_t len = strlen(str);
size_t sublen = strlen(substr);
if (sublen > len)
return false;
return !strcmp(str + len - sublen, substr);
}
extern char * __must_check skip_spaces(const char *);
extern char *strim(char *);

View File

@ -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
objtool-y += libstring.o
objtool-y += libctype.o

View File

@ -15,13 +15,14 @@ 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
export BUILD_ORC BUILD_KLP
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))

View File

@ -88,6 +88,46 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *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)
{
return insn->offset + insn->len + insn->immediate;

View File

@ -0,0 +1,52 @@
// 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, },
};
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;
}

View File

@ -185,20 +185,6 @@ static bool is_sibling_call(struct instruction *insn)
return (is_static_jump(insn) && insn_call_dest(insn));
}
/*
* Checks if a string ends with another.
*/
static bool str_ends_with(const char *s, const char *sub)
{
const int slen = strlen(s);
const int sublen = strlen(sub);
if (sublen > slen)
return 0;
return !memcmp(s + slen - sublen, sub, sublen);
}
/*
* Checks if a function is a Rust "noreturn" one.
*/

View File

@ -288,6 +288,18 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
return NULL;
}
struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name)
{
struct symbol *sym;
elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
if (!strcmp(sym->name, name) && !is_local_sym(sym))
return sym;
}
return NULL;
}
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len)
{
@ -475,6 +487,8 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
else
entry = &sym->sec->symbol_list;
list_add(&sym->list, entry);
list_add_tail(&sym->global_list, &elf->symbols);
elf_hash_add(symbol, &sym->hash, sym->idx);
elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
@ -531,6 +545,9 @@ static int read_symbols(struct elf *elf)
ERROR_GLIBC("calloc");
return -1;
}
INIT_LIST_HEAD(&elf->symbols);
for (i = 0; i < symbols_nr; i++) {
sym = &elf->symbol_data[i];
@ -639,7 +656,7 @@ static int mark_group_syms(struct elf *elf)
return -1;
}
list_for_each_entry(sec, &elf->sections, list) {
for_each_sec(elf, sec) {
if (sec->sh.sh_type == SHT_GROUP &&
sec->sh.sh_link == symtab->idx) {
sym = find_symbol_by_index(elf, sec->sh.sh_info);
@ -1224,6 +1241,8 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
return NULL;
}
INIT_LIST_HEAD(&elf->symbols);
if (!elf_alloc_hash(section, 1000) ||
!elf_alloc_hash(section_name, 1000) ||
!elf_alloc_hash(symbol, 10000) ||

View File

@ -84,6 +84,7 @@ bool arch_callee_saved_reg(unsigned char reg);
unsigned long arch_jump_destination(struct instruction *insn);
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);

View File

@ -53,4 +53,6 @@ int objtool_run(int argc, const char **argv);
int make_backup(void);
int cmd_klp(int argc, const char **argv);
#endif /* _BUILTIN_H */

View File

@ -18,6 +18,7 @@
#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)
@ -53,10 +54,12 @@ struct section {
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;
@ -83,10 +86,13 @@ struct symbol {
u8 cold : 1;
u8 prefix : 1;
u8 debug_checksum : 1;
u8 changed : 1;
u8 included : 1;
struct list_head pv_target;
struct reloc *relocs;
struct section *group_sec;
struct checksum csum;
struct symbol *twin, *clone;
};
struct reloc {
@ -104,6 +110,7 @@ struct elf {
const char *name, *tmp_name;
unsigned int num_files;
struct list_head sections;
struct list_head symbols;
unsigned long num_relocs;
int symbol_bits;
@ -179,6 +186,7 @@ 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);
@ -448,22 +456,48 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
#define sec_for_each_sym(sec, sym) \
list_for_each_entry(sym, &sec->symbol_list, list)
#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) \
for (struct section *__sec, *__fake = (struct section *)1; \
__fake; __fake = NULL) \
for_each_sec(elf, __sec) \
sec_for_each_sym(__sec, 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)

View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _OBJTOOL_KLP_H
#define _OBJTOOL_KLP_H
/*
* __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);
#endif /* _OBJTOOL_KLP_H */

View File

@ -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);

View File

@ -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 */

1646
tools/objtool/klp-diff.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,8 +16,6 @@
#include <objtool/objtool.h>
#include <objtool/warn.h>
bool help;
static struct objtool_file file;
struct objtool_file *objtool_open_read(const char *filename)
@ -71,6 +69,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 +110,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);
}

View File

@ -17,6 +17,7 @@ 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
"

View File

@ -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");
}