create-diff-object: detect unsupported static calls

Similar to jump labels, static calls aren't supported when the static
call key was originally defined in a module rather than in vmlinux.
Detect those cases and either remove them (in the case of tracepoints)
or error out.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
This commit is contained in:
Josh Poimboeuf 2022-11-21 19:29:53 -08:00
parent 56bd8c4d0d
commit f83218ad12
1 changed files with 68 additions and 1 deletions

View File

@ -71,7 +71,7 @@ enum loglevel loglevel = NORMAL;
bool KLP_ARCH;
int jump_label_errors;
int jump_label_errors, static_call_errors;
/*******************
* Data structures
@ -2321,6 +2321,68 @@ static bool jump_table_group_filter(struct lookup_table *lookup,
return true;
}
static bool static_call_sites_group_filter(struct lookup_table *lookup,
struct section *relasec,
unsigned int group_offset,
unsigned int group_size)
{
struct rela *code = NULL, *key = NULL, *rela;
bool tracepoint = false;
struct lookup_result symbol;
int i = 0;
/*
* Here we hard-code knowledge about the contents of the jump_entry
* struct. It has three fields: code, target, and key. Each field has
* a relocation associated with it.
*/
list_for_each_entry(rela, &relasec->relas, list) {
if (rela->offset >= group_offset &&
rela->offset < group_offset + group_size) {
if (i == 0)
code = rela;
else if (i == 1)
key = rela;
i++;
}
}
if (i != 2 || !key || !code)
ERROR("BUG: .static_call_sites has an unexpected format");
if (!strncmp(key->sym->name, "__SCK__tp_func_", 15))
tracepoint = true;
/*
* Static calls are only supported in the case where the corresponding
* static call key lives in vmlinux (see explanation in
* jump_table_group_filter).
*/
if (lookup_symbol(lookup, key->sym, &symbol) &&
strcmp(symbol.objname, "vmlinux")) {
/* The key lives in a module -- not supported */
/* Inert tracepoints are harmless */
if (tracepoint)
return false;
/*
* This will be upgraded to an error after all static call
* errors have been reported.
*/
log_normal("Found a static call at %s()+0x%lx, using key %s, which is defined in a module. Use KPATCH_STATIC_CALL() instead.\n",
code->sym->name, code->addend, key->sym->name);
static_call_errors++;
return false;
}
/* The key lives in vmlinux or the patch module itself */
return true;
}
static struct special_section special_sections[] = {
{
.name = "__bug_table",
@ -2367,6 +2429,7 @@ static struct special_section special_sections[] = {
.name = ".static_call_sites",
.arch = X86_64,
.group_size = static_call_sites_group_size,
.group_filter = static_call_sites_group_filter,
},
{
.name = ".retpoline_sites",
@ -2564,6 +2627,10 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf,
ERROR("Found %d unsupported jump label(s) in the patched code. Use static_key_enabled() instead.",
jump_label_errors);
if (static_call_errors)
ERROR("Found %d unsupported static call(s) in the patched code. Use KPATCH_STATIC_CALL() instead.",
static_call_errors);
if (!dest_offset) {
/* no changed or global functions referenced */
relasec->status = relasec->base->status = SAME;