mirror of https://github.com/torvalds/linux.git
objtool: Add base objtool support for livepatch modules
In preparation for klp-build, enable "classic" objtool to work on
livepatch modules:
- Avoid duplicate symbol/section warnings for prefix symbols and the
.static_call_sites and __mcount_loc sections which may have already
been extracted by klp diff.
- Add __klp_funcs to the IBT function pointer section whitelist.
- Prevent KLP symbols from getting incorrectly classified as cold
subfunctions.
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:
parent
2058f6d166
commit
164c9201e1
|
|
@ -3,6 +3,7 @@
|
||||||
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE /* memmem() */
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
@ -611,6 +612,20 @@ static int init_pv_ops(struct objtool_file *file)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_livepatch_module(struct objtool_file *file)
|
||||||
|
{
|
||||||
|
struct section *sec;
|
||||||
|
|
||||||
|
if (!opts.module)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sec = find_section_by_name(file->elf, ".modinfo");
|
||||||
|
if (!sec)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return memmem(sec->data->d_buf, sec_size(sec), "\0livepatch=Y", 12);
|
||||||
|
}
|
||||||
|
|
||||||
static int create_static_call_sections(struct objtool_file *file)
|
static int create_static_call_sections(struct objtool_file *file)
|
||||||
{
|
{
|
||||||
struct static_call_site *site;
|
struct static_call_site *site;
|
||||||
|
|
@ -622,7 +637,14 @@ static int create_static_call_sections(struct objtool_file *file)
|
||||||
|
|
||||||
sec = find_section_by_name(file->elf, ".static_call_sites");
|
sec = find_section_by_name(file->elf, ".static_call_sites");
|
||||||
if (sec) {
|
if (sec) {
|
||||||
|
/*
|
||||||
|
* Livepatch modules may have already extracted the static call
|
||||||
|
* site entries to take advantage of vmlinux static call
|
||||||
|
* privileges.
|
||||||
|
*/
|
||||||
|
if (!file->klp)
|
||||||
WARN("file already has .static_call_sites section, skipping");
|
WARN("file already has .static_call_sites section, skipping");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -666,7 +688,7 @@ static int create_static_call_sections(struct objtool_file *file)
|
||||||
|
|
||||||
key_sym = find_symbol_by_name(file->elf, tmp);
|
key_sym = find_symbol_by_name(file->elf, tmp);
|
||||||
if (!key_sym) {
|
if (!key_sym) {
|
||||||
if (!opts.module) {
|
if (!opts.module || file->klp) {
|
||||||
ERROR("static_call: can't find static_call_key symbol: %s", tmp);
|
ERROR("static_call: can't find static_call_key symbol: %s", tmp);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -885,7 +907,13 @@ static int create_mcount_loc_sections(struct objtool_file *file)
|
||||||
|
|
||||||
sec = find_section_by_name(file->elf, "__mcount_loc");
|
sec = find_section_by_name(file->elf, "__mcount_loc");
|
||||||
if (sec) {
|
if (sec) {
|
||||||
|
/*
|
||||||
|
* Livepatch modules have already extracted their __mcount_loc
|
||||||
|
* entries to cover the !CONFIG_FTRACE_MCOUNT_USE_OBJTOOL case.
|
||||||
|
*/
|
||||||
|
if (!file->klp)
|
||||||
WARN("file already has __mcount_loc section, skipping");
|
WARN("file already has __mcount_loc section, skipping");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2569,6 +2597,8 @@ static bool validate_branch_enabled(void)
|
||||||
|
|
||||||
static int decode_sections(struct objtool_file *file)
|
static int decode_sections(struct objtool_file *file)
|
||||||
{
|
{
|
||||||
|
file->klp = is_livepatch_module(file);
|
||||||
|
|
||||||
mark_rodata(file);
|
mark_rodata(file);
|
||||||
|
|
||||||
if (init_pv_ops(file))
|
if (init_pv_ops(file))
|
||||||
|
|
@ -4244,6 +4274,9 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
|
||||||
* - compiler cloned functions (*.cold, *.part0, etc)
|
* - compiler cloned functions (*.cold, *.part0, etc)
|
||||||
* - asm functions created with inline asm or without SYM_FUNC_START()
|
* - asm functions created with inline asm or without SYM_FUNC_START()
|
||||||
*
|
*
|
||||||
|
* Also, the function may already have a prefix from a previous objtool run
|
||||||
|
* (livepatch extracted functions, or manually running objtool multiple times).
|
||||||
|
*
|
||||||
* So return 0 if the NOPs are missing or the function already has a prefix
|
* So return 0 if the NOPs are missing or the function already has a prefix
|
||||||
* symbol.
|
* symbol.
|
||||||
*/
|
*/
|
||||||
|
|
@ -4266,6 +4299,14 @@ static int create_prefix_symbol(struct objtool_file *file, struct symbol *func)
|
||||||
if (snprintf_check(name, SYM_NAME_LEN, "__pfx_%s", func->name))
|
if (snprintf_check(name, SYM_NAME_LEN, "__pfx_%s", func->name))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (file->klp) {
|
||||||
|
struct symbol *pfx;
|
||||||
|
|
||||||
|
pfx = find_symbol_by_offset(func->sec, func->offset - opts.prefix);
|
||||||
|
if (pfx && is_prefix_func(pfx) && !strcmp(pfx->name, name))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
insn = find_insn(file, func->sec, func->offset);
|
insn = find_insn(file, func->sec, func->offset);
|
||||||
if (!insn) {
|
if (!insn) {
|
||||||
WARN("%s: can't find starting instruction", func->name);
|
WARN("%s: can't find starting instruction", func->name);
|
||||||
|
|
@ -4618,6 +4659,7 @@ static int validate_ibt(struct objtool_file *file)
|
||||||
!strncmp(sec->name, ".debug", 6) ||
|
!strncmp(sec->name, ".debug", 6) ||
|
||||||
!strcmp(sec->name, ".altinstructions") ||
|
!strcmp(sec->name, ".altinstructions") ||
|
||||||
!strcmp(sec->name, ".ibt_endbr_seal") ||
|
!strcmp(sec->name, ".ibt_endbr_seal") ||
|
||||||
|
!strcmp(sec->name, ".kcfi_traps") ||
|
||||||
!strcmp(sec->name, ".orc_unwind_ip") ||
|
!strcmp(sec->name, ".orc_unwind_ip") ||
|
||||||
!strcmp(sec->name, ".retpoline_sites") ||
|
!strcmp(sec->name, ".retpoline_sites") ||
|
||||||
!strcmp(sec->name, ".smp_locks") ||
|
!strcmp(sec->name, ".smp_locks") ||
|
||||||
|
|
@ -4627,12 +4669,12 @@ static int validate_ibt(struct objtool_file *file)
|
||||||
!strcmp(sec->name, "__bug_table") ||
|
!strcmp(sec->name, "__bug_table") ||
|
||||||
!strcmp(sec->name, "__ex_table") ||
|
!strcmp(sec->name, "__ex_table") ||
|
||||||
!strcmp(sec->name, "__jump_table") ||
|
!strcmp(sec->name, "__jump_table") ||
|
||||||
|
!strcmp(sec->name, "__klp_funcs") ||
|
||||||
!strcmp(sec->name, "__mcount_loc") ||
|
!strcmp(sec->name, "__mcount_loc") ||
|
||||||
!strcmp(sec->name, ".kcfi_traps") ||
|
|
||||||
!strcmp(sec->name, ".llvm.call-graph-profile") ||
|
!strcmp(sec->name, ".llvm.call-graph-profile") ||
|
||||||
!strcmp(sec->name, ".llvm_bb_addr_map") ||
|
!strcmp(sec->name, ".llvm_bb_addr_map") ||
|
||||||
!strcmp(sec->name, "__tracepoints") ||
|
!strcmp(sec->name, "__tracepoints") ||
|
||||||
strstr(sec->name, "__patchable_function_entries"))
|
!strcmp(sec->name, "__patchable_function_entries"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for_each_reloc(sec->rsec, reloc)
|
for_each_reloc(sec->rsec, reloc)
|
||||||
|
|
|
||||||
|
|
@ -499,7 +499,10 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
|
||||||
strstarts(sym->name, "__pi___cfi_")))
|
strstarts(sym->name, "__pi___cfi_")))
|
||||||
sym->prefix = 1;
|
sym->prefix = 1;
|
||||||
|
|
||||||
if (is_func_sym(sym) && strstr(sym->name, ".cold"))
|
if (strstarts(sym->name, ".klp.sym"))
|
||||||
|
sym->klp = 1;
|
||||||
|
|
||||||
|
if (!sym->klp && is_func_sym(sym) && strstr(sym->name, ".cold"))
|
||||||
sym->cold = 1;
|
sym->cold = 1;
|
||||||
sym->pfunc = sym->cfunc = sym;
|
sym->pfunc = sym->cfunc = sym;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ struct symbol {
|
||||||
u8 debug_checksum : 1;
|
u8 debug_checksum : 1;
|
||||||
u8 changed : 1;
|
u8 changed : 1;
|
||||||
u8 included : 1;
|
u8 included : 1;
|
||||||
|
u8 klp : 1;
|
||||||
struct list_head pv_target;
|
struct list_head pv_target;
|
||||||
struct reloc *relocs;
|
struct reloc *relocs;
|
||||||
struct section *group_sec;
|
struct section *group_sec;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ struct objtool_file {
|
||||||
struct list_head mcount_loc_list;
|
struct list_head mcount_loc_list;
|
||||||
struct list_head endbr_list;
|
struct list_head endbr_list;
|
||||||
struct list_head call_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;
|
||||||
unsigned int nr_endbr_int;
|
unsigned int nr_endbr_int;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue