@@ -75,6 +75,7 @@ const struct option check_options[] = {
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"),
@@ -3417,7 +3417,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
/* Ignore KCFI type preambles, which always fall through */
- if (!strncmp(func->name, "__cfi_", 6))
+ if (!strncmp(func->name, "__cfi_", 6) ||
+ !strncmp(func->name, "__pfx_", 6))
return 0;
WARN("%s() falls through to next function %s()",
@@ -3972,6 +3973,34 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
return false;
}
+static int add_prefix_symbol(struct objtool_file *file, struct symbol *func,
+ struct instruction *insn)
+{
+ if (!opts.prefix)
+ return 0;
+
+ for (;;) {
+ struct instruction *prev = list_prev_entry(insn, list);
+ u64 offset;
+
+ if (&prev->list == &file->insn_list)
+ break;
+
+ if (prev->type != INSN_NOP)
+ break;
+
+ offset = func->offset - prev->offset;
+ if (offset >= opts.prefix) {
+ if (offset == opts.prefix)
+ elf_create_prefix_symbol(file->elf, func, opts.prefix);
+ break;
+ }
+ insn = prev;
+ }
+
+ return 0;
+}
+
static int validate_symbol(struct objtool_file *file, struct section *sec,
struct symbol *sym, struct insn_state *state)
{
@@ -3990,6 +4019,8 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
if (!insn || insn->ignore || insn->visited)
return 0;
+ add_prefix_symbol(file, sym, insn);
+
state->uaccess = sym->uaccess_safe;
ret = validate_branch(file, insn_func(insn), insn, *state);
@@ -819,6 +819,37 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)
return sym;
}
+static int elf_add_string(struct elf *elf, struct section *strtab, char *str);
+
+struct symbol *
+elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
+{
+ struct symbol *sym = calloc(1, sizeof(*sym));
+ size_t namelen = strlen(orig->name) + sizeof("__pfx_");
+ char *name = malloc(namelen);
+
+ if (!sym || !name) {
+ perror("malloc");
+ return NULL;
+ }
+
+ snprintf(name, namelen, "__pfx_%s", orig->name);
+
+ sym->name = name;
+ sym->sec = orig->sec;
+
+ sym->sym.st_name = elf_add_string(elf, NULL, name);
+ sym->sym.st_info = orig->sym.st_info;
+ sym->sym.st_value = orig->sym.st_value - size;
+ sym->sym.st_size = size;
+
+ sym = __elf_create_symbol(elf, sym);
+ if (sym)
+ elf_add_symbol(elf, sym);
+
+ return sym;
+}
+
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int type,
struct section *insn_sec, unsigned long insn_off)
@@ -26,6 +26,7 @@ struct opts {
bool stackval;
bool static_call;
bool uaccess;
+ int prefix;
/* options: */
bool backtrace;
@@ -146,6 +146,8 @@ static inline bool has_multiple_files(struct elf *elf)
struct elf *elf_open_read(const char *name, int flags);
struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr);
+struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
+
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, s64 addend);
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,