summaryrefslogtreecommitdiff
path: root/tools/objtool
diff options
context:
space:
mode:
Diffstat (limited to 'tools/objtool')
-rw-r--r--tools/objtool/Makefile12
-rw-r--r--tools/objtool/arch/x86/decode.c80
-rw-r--r--tools/objtool/arch/x86/orc.c31
-rw-r--r--tools/objtool/check.c38
-rw-r--r--tools/objtool/disas.c6
-rw-r--r--tools/objtool/elf.c126
-rw-r--r--tools/objtool/include/objtool/elf.h3
-rw-r--r--tools/objtool/include/objtool/warn.h2
-rw-r--r--tools/objtool/klp-diff.c139
9 files changed, 295 insertions, 142 deletions
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index a40f30232929..b71d1886022e 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -13,7 +13,7 @@ endif
ifeq ($(ARCH_HAS_KLP),y)
HAVE_XXHASH = $(shell printf "$(pound)include <xxhash.h>\nXXH3_state_t *state;int main() {}" | \
- $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n)
+ $(HOSTCC) $(HOSTCFLAGS) -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) \
@@ -29,6 +29,8 @@ srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
+RM ?= rm -f
+
LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/
ifneq ($(OUTPUT),)
LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
@@ -140,13 +142,15 @@ $(LIBSUBCMD)-clean:
$(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT)
clean: $(LIBSUBCMD)-clean
- $(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
- $(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
+ $(Q)find $(OUTPUT) \( -name '*.o' -o -name '\.*.cmd' -o -name '\.*.d' \) -type f -print | xargs $(RM)
$(Q)$(RM) $(OUTPUT)arch/x86/lib/cpu-feature-names.c $(OUTPUT)fixdep
$(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
$(Q)$(RM) -r -- $(OUTPUT)feature
+mrproper: clean
+ $(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
+
FORCE:
-.PHONY: clean FORCE
+.PHONY: clean mrproper FORCE
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 73bfea220d1b..350b8ee6e776 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -395,52 +395,36 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (!rex_w)
break;
- if (modrm_reg == CFI_SP) {
-
- if (mod_is_reg()) {
- /* mov %rsp, reg */
- ADD_OP(op) {
- op->src.type = OP_SRC_REG;
- op->src.reg = CFI_SP;
- op->dest.type = OP_DEST_REG;
- op->dest.reg = modrm_rm;
- }
- break;
-
- } else {
- /* skip RIP relative displacement */
- if (is_RIP())
- break;
-
- /* skip nontrivial SIB */
- if (have_SIB()) {
- modrm_rm = sib_base;
- if (sib_index != CFI_SP)
- break;
- }
-
- /* mov %rsp, disp(%reg) */
- ADD_OP(op) {
- op->src.type = OP_SRC_REG;
- op->src.reg = CFI_SP;
- op->dest.type = OP_DEST_REG_INDIRECT;
- op->dest.reg = modrm_rm;
- op->dest.offset = ins.displacement.value;
- }
- break;
+ if (mod_is_reg()) {
+ /* mov reg, reg */
+ ADD_OP(op) {
+ op->src.type = OP_SRC_REG;
+ op->src.reg = modrm_reg;
+ op->dest.type = OP_DEST_REG;
+ op->dest.reg = modrm_rm;
}
-
break;
}
- if (rm_is_reg(CFI_SP)) {
+ /* skip RIP relative displacement */
+ if (is_RIP())
+ break;
+
+ /* skip nontrivial SIB */
+ if (have_SIB()) {
+ modrm_rm = sib_base;
+ if (sib_index != CFI_SP)
+ break;
+ }
- /* mov reg, %rsp */
+ /* mov %rsp, disp(%reg) */
+ if (modrm_reg == CFI_SP) {
ADD_OP(op) {
op->src.type = OP_SRC_REG;
- op->src.reg = modrm_reg;
- op->dest.type = OP_DEST_REG;
- op->dest.reg = CFI_SP;
+ op->src.reg = CFI_SP;
+ op->dest.type = OP_DEST_REG_INDIRECT;
+ op->dest.reg = modrm_rm;
+ op->dest.offset = ins.displacement.value;
}
break;
}
@@ -891,14 +875,20 @@ int arch_decode_hint_reg(u8 sp_reg, int *base)
case ORC_REG_UNDEFINED:
*base = CFI_UNDEFINED;
break;
+ case ORC_REG_AX:
+ *base = CFI_AX;
+ break;
+ case ORC_REG_DX:
+ *base = CFI_DX;
+ break;
case ORC_REG_SP:
*base = CFI_SP;
break;
case ORC_REG_BP:
*base = CFI_BP;
break;
- case ORC_REG_SP_INDIRECT:
- *base = CFI_SP_INDIRECT;
+ case ORC_REG_DI:
+ *base = CFI_DI;
break;
case ORC_REG_R10:
*base = CFI_R10;
@@ -906,11 +896,11 @@ int arch_decode_hint_reg(u8 sp_reg, int *base)
case ORC_REG_R13:
*base = CFI_R13;
break;
- case ORC_REG_DI:
- *base = CFI_DI;
+ case ORC_REG_SP_INDIRECT:
+ *base = CFI_SP_INDIRECT;
break;
- case ORC_REG_DX:
- *base = CFI_DX;
+ case ORC_REG_BP_INDIRECT:
+ *base = CFI_BP_INDIRECT;
break;
default:
return -1;
diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c
index 735e150ca6b7..eff078ecc945 100644
--- a/tools/objtool/arch/x86/orc.c
+++ b/tools/objtool/arch/x86/orc.c
@@ -46,17 +46,20 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
orc->signal = cfi->signal;
switch (cfi->cfa.base) {
+ case CFI_AX:
+ orc->sp_reg = ORC_REG_AX;
+ break;
+ case CFI_DX:
+ orc->sp_reg = ORC_REG_DX;
+ break;
case CFI_SP:
orc->sp_reg = ORC_REG_SP;
break;
- case CFI_SP_INDIRECT:
- orc->sp_reg = ORC_REG_SP_INDIRECT;
- break;
case CFI_BP:
orc->sp_reg = ORC_REG_BP;
break;
- case CFI_BP_INDIRECT:
- orc->sp_reg = ORC_REG_BP_INDIRECT;
+ case CFI_DI:
+ orc->sp_reg = ORC_REG_DI;
break;
case CFI_R10:
orc->sp_reg = ORC_REG_R10;
@@ -64,11 +67,11 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct
case CFI_R13:
orc->sp_reg = ORC_REG_R13;
break;
- case CFI_DI:
- orc->sp_reg = ORC_REG_DI;
+ case CFI_SP_INDIRECT:
+ orc->sp_reg = ORC_REG_SP_INDIRECT;
break;
- case CFI_DX:
- orc->sp_reg = ORC_REG_DX;
+ case CFI_BP_INDIRECT:
+ orc->sp_reg = ORC_REG_BP_INDIRECT;
break;
default:
ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
@@ -122,22 +125,24 @@ static const char *reg_name(unsigned int reg)
switch (reg) {
case ORC_REG_PREV_SP:
return "prevsp";
+ case ORC_REG_AX:
+ return "ax";
case ORC_REG_DX:
return "dx";
- case ORC_REG_DI:
- return "di";
case ORC_REG_BP:
return "bp";
case ORC_REG_SP:
return "sp";
+ case ORC_REG_DI:
+ return "di";
case ORC_REG_R10:
return "r10";
case ORC_REG_R13:
return "r13";
- case ORC_REG_BP_INDIRECT:
- return "bp(ind)";
case ORC_REG_SP_INDIRECT:
return "sp(ind)";
+ case ORC_REG_BP_INDIRECT:
+ return "bp(ind)";
default:
return "?";
}
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 37f87c4a0134..9b11cf3193b9 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -197,7 +197,8 @@ static bool is_rust_noreturn(const struct symbol *func)
* as well as changes to the source code itself between versions (since
* these come from the Rust standard library).
*/
- return str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") ||
+ return str_ends_with(func->name, "_4core3num20from_str_radix_panic") ||
+ str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") ||
str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") ||
str_ends_with(func->name, "_4core6option13expect_failed") ||
str_ends_with(func->name, "_4core6option13unwrap_failed") ||
@@ -1300,7 +1301,7 @@ static const char *uaccess_safe_builtin[] = {
"copy_mc_enhanced_fast_string",
"rep_stos_alternative",
"rep_movs_alternative",
- "__copy_user_nocache",
+ "copy_to_nontemporal",
NULL
};
@@ -2183,12 +2184,11 @@ static void mark_func_jump_tables(struct objtool_file *file,
last = insn;
/*
- * Store back-pointers for unconditional forward jumps such
+ * Store back-pointers for forward jumps such
* that find_jump_table() can back-track using those and
* avoid some potentially confusing code.
*/
- if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
- insn->offset > last->offset &&
+ if (insn->jump_dest &&
insn->jump_dest->offset > insn->offset &&
!insn->jump_dest->first_jump_src) {
@@ -2999,6 +2999,20 @@ static int update_cfi_state(struct instruction *insn,
cfi->stack_size += 8;
}
+ else if (cfi->vals[op->src.reg].base == CFI_CFA) {
+ /*
+ * Clang RSP musical chairs:
+ *
+ * mov %rsp, %rdx [handled above]
+ * ...
+ * mov %rdx, %rbx [handled here]
+ * ...
+ * mov %rbx, %rsp [handled above]
+ */
+ cfi->vals[op->dest.reg].base = CFI_CFA;
+ cfi->vals[op->dest.reg].offset = cfi->vals[op->src.reg].offset;
+ }
+
break;
@@ -3733,7 +3747,7 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
static int validate_branch(struct objtool_file *file, struct symbol *func,
struct instruction *insn, struct insn_state state);
static int do_validate_branch(struct objtool_file *file, struct symbol *func,
- struct instruction *insn, struct insn_state state);
+ struct instruction *insn, struct insn_state *state);
static int validate_insn(struct objtool_file *file, struct symbol *func,
struct instruction *insn, struct insn_state *statep,
@@ -3998,7 +4012,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
* tools/objtool/Documentation/objtool.txt.
*/
static int do_validate_branch(struct objtool_file *file, struct symbol *func,
- struct instruction *insn, struct insn_state state)
+ struct instruction *insn, struct insn_state *state)
{
struct instruction *next_insn, *prev_insn = NULL;
bool dead_end;
@@ -4029,7 +4043,7 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func,
return 1;
}
- ret = validate_insn(file, func, insn, &state, prev_insn, next_insn,
+ ret = validate_insn(file, func, insn, state, prev_insn, next_insn,
&dead_end);
if (!insn->trace) {
@@ -4040,7 +4054,7 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func,
}
if (!dead_end && !next_insn) {
- if (state.cfi.cfa.base == CFI_UNDEFINED)
+ if (state->cfi.cfa.base == CFI_UNDEFINED)
return 0;
if (file->ignore_unreachables)
return 0;
@@ -4065,7 +4079,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
int ret;
trace_depth_inc();
- ret = do_validate_branch(file, func, insn, state);
+ ret = do_validate_branch(file, func, insn, &state);
trace_depth_dec();
return ret;
@@ -4292,8 +4306,8 @@ static int validate_retpoline(struct objtool_file *file)
list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
struct symbol *sym = insn->sym;
- if (sym && (sym->type == STT_NOTYPE ||
- sym->type == STT_FUNC) && !sym->nocfi) {
+ if (sym && (is_notype_sym(sym) ||
+ is_func_sym(sym)) && !sym->nocfi) {
struct instruction *prev =
prev_insn_same_sym(file, insn);
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 26f08d41f2b1..59090234af19 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -264,7 +264,7 @@ static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
* If the relocation symbol is a section name (for example ".bss")
* then we try to further resolve the name.
*/
- if (reloc->sym->type == STT_SECTION) {
+ if (is_sec_sym(reloc->sym)) {
str = offstr(reloc->sym->sec, reloc->sym->offset + offset);
DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str);
free(str);
@@ -580,7 +580,7 @@ static size_t disas_insn_common(struct disas_context *dctx,
*/
dinfo->buffer = insn->sec->data->d_buf;
dinfo->buffer_vma = 0;
- dinfo->buffer_length = insn->sec->sh.sh_size;
+ dinfo->buffer_length = sec_size(insn->sec);
return disasm(insn->offset, &dctx->info);
}
@@ -1231,7 +1231,7 @@ void disas_funcs(struct disas_context *dctx)
for_each_sec(dctx->file->elf, sec) {
- if (!(sec->sh.sh_flags & SHF_EXECINSTR))
+ if (!is_text_sec(sec))
continue;
sec_for_each_sym(sec, sym) {
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 2c02c7b49265..f3df2bde119f 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -16,7 +16,6 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <libgen.h>
#include <ctype.h>
#include <linux/align.h>
#include <linux/kernel.h>
@@ -26,11 +25,18 @@
#include <objtool/elf.h>
#include <objtool/warn.h>
+static ssize_t demangled_name_len(const char *name);
+
static inline u32 str_hash(const char *str)
{
return jhash(str, strlen(str), 0);
}
+static inline u32 str_hash_demangled(const char *str)
+{
+ return jhash(str, demangled_name_len(str), 0);
+}
+
#define __elf_table(name) (elf->name##_hash)
#define __elf_bits(name) (elf->name##_bits)
@@ -294,7 +300,7 @@ static struct symbol *find_local_symbol_by_file_and_name(const struct elf *elf,
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
+ elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
if (sym->bind == STB_LOCAL && sym->file == file &&
!strcmp(sym->name, name)) {
return sym;
@@ -308,7 +314,7 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
+ elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
if (!strcmp(sym->name, name) && !is_local_sym(sym))
return sym;
}
@@ -316,6 +322,19 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
return NULL;
}
+void iterate_global_symbol_by_demangled_name(const struct elf *elf,
+ const char *demangled_name,
+ void (*process)(struct symbol *sym, void *data),
+ void *data)
+{
+ struct symbol *sym;
+
+ elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(demangled_name)) {
+ if (!strcmp(sym->demangled_name, demangled_name) && !is_local_sym(sym))
+ process(sym, data);
+ }
+}
+
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len)
{
@@ -441,34 +460,67 @@ static int read_sections(struct elf *elf)
return 0;
}
+/*
+ * Returns desired length of the demangled name.
+ * If name doesn't need demangling, return strlen(name).
+ */
+static ssize_t demangled_name_len(const char *name)
+{
+ ssize_t idx;
+ const char *p;
+
+ p = strstr(name, ".llvm.");
+ if (p)
+ return p - name;
+
+ if (!strstarts(name, "__UNIQUE_ID_") && !strchr(name, '.'))
+ return strlen(name);
+
+ for (idx = strlen(name) - 1; idx >= 0; idx--) {
+ char c = name[idx];
+
+ if (!isdigit(c) && c != '.' && c != '_')
+ break;
+ }
+ if (idx <= 0)
+ return strlen(name);
+ return idx + 1;
+}
+
+/*
+ * Remove number suffix of a symbol.
+ *
+ * Specifically, remove trailing numbers for "__UNIQUE_ID_" symbols and
+ * symbols with '.'.
+ *
+ * With CONFIG_LTO_CLANG_THIN, it is possible to have nested __UNIQUE_ID_,
+ * such as
+ *
+ * __UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695
+ *
+ * to remove both trailing numbers, also remove trailing '_'.
+ *
+ * For symbols with llvm suffix, i.e., foo.llvm.<hash>, remove the
+ * .llvm.<hash> part.
+ */
static const char *demangle_name(struct symbol *sym)
{
char *str;
-
- if (!is_local_sym(sym))
- return sym->name;
+ ssize_t len;
if (!is_func_sym(sym) && !is_object_sym(sym))
return sym->name;
- if (!strstarts(sym->name, "__UNIQUE_ID_") && !strchr(sym->name, '.'))
+ len = demangled_name_len(sym->name);
+ if (len == strlen(sym->name))
return sym->name;
- str = strdup(sym->name);
+ str = strndup(sym->name, len);
if (!str) {
ERROR_GLIBC("strdup");
return NULL;
}
- for (int i = strlen(str) - 1; i >= 0; i--) {
- char c = str[i];
-
- if (!isdigit(c) && c != '.') {
- str[i + 1] = '\0';
- break;
- }
- }
-
return str;
}
@@ -504,9 +556,13 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
entry = &sym->sec->symbol_list;
list_add(&sym->list, entry);
+ sym->demangled_name = demangle_name(sym);
+ if (!sym->demangled_name)
+ return -1;
+
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));
+ elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->demangled_name));
if (is_func_sym(sym) &&
(strstarts(sym->name, "__pfx_") ||
@@ -530,10 +586,6 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
sym->pfunc = sym->cfunc = sym;
- sym->demangled_name = demangle_name(sym);
- if (!sym->demangled_name)
- return -1;
-
return 0;
}
@@ -614,7 +666,7 @@ static int read_symbols(struct elf *elf)
if (elf_add_symbol(elf, sym))
return -1;
- if (sym->type == STT_FILE)
+ if (is_file_sym(sym))
file = sym;
else if (sym->bind == STB_LOCAL)
sym->file = file;
@@ -1189,7 +1241,7 @@ err:
struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
{
struct section *null, *symtab, *strtab, *shstrtab;
- char *dir, *base, *tmp_name;
+ char *tmp_name;
struct symbol *sym;
struct elf *elf;
@@ -1203,29 +1255,13 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
INIT_LIST_HEAD(&elf->sections);
- dir = strdup(name);
- if (!dir) {
- ERROR_GLIBC("strdup");
- return NULL;
- }
-
- dir = dirname(dir);
-
- base = strdup(name);
- if (!base) {
- ERROR_GLIBC("strdup");
- return NULL;
- }
-
- base = basename(base);
-
- tmp_name = malloc(256);
+ tmp_name = malloc(strlen(name) + 8);
if (!tmp_name) {
ERROR_GLIBC("malloc");
return NULL;
}
- snprintf(tmp_name, 256, "%s/%s.XXXXXX", dir, base);
+ sprintf(tmp_name, "%s.XXXXXX", name);
elf->fd = mkstemp(tmp_name);
if (elf->fd == -1) {
@@ -1335,7 +1371,7 @@ unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char
return -1;
}
- offset = ALIGN(strtab->sh.sh_size, strtab->sh.sh_addralign);
+ offset = ALIGN(sec_size(strtab), strtab->sh.sh_addralign);
if (!elf_add_data(elf, strtab, str, strlen(str) + 1))
return -1;
@@ -1375,9 +1411,9 @@ void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_
memcpy(sec->data->d_buf, data, size);
sec->data->d_size = size;
- sec->data->d_align = 1;
+ sec->data->d_align = sec->sh.sh_addralign;
- offset = ALIGN(sec->sh.sh_size, sec->sh.sh_addralign);
+ offset = ALIGN(sec_size(sec), sec->sh.sh_addralign);
sec->sh.sh_size = offset + size;
mark_sec_changed(elf, sec, true);
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index e12c516bd320..25573e5af76e 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -186,6 +186,9 @@ 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);
+void iterate_global_symbol_by_demangled_name(const struct elf *elf, const char *demangled_name,
+ void (*process)(struct symbol *sym, void *data),
+ void *data);
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);
diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h
index 2b27b54096b8..fa8b7d292e83 100644
--- a/tools/objtool/include/objtool/warn.h
+++ b/tools/objtool/include/objtool/warn.h
@@ -107,7 +107,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
#define ERROR_ELF(format, ...) __WARN_ELF(ERROR_STR, format, ##__VA_ARGS__)
#define ERROR_GLIBC(format, ...) __WARN_GLIBC(ERROR_STR, format, ##__VA_ARGS__)
#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__)
+#define ERROR_INSN(insn, format, ...) ERROR_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
extern bool debug;
extern int indent;
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 9f1f4011eb9c..0b0d1503851f 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -14,6 +14,7 @@
#include <objtool/util.h>
#include <arch/special.h>
+#include <linux/align.h>
#include <linux/objtool_types.h>
#include <linux/livepatch_external.h>
#include <linux/stringify.h>
@@ -271,7 +272,7 @@ static bool is_uncorrelated_static_local(struct symbol *sym)
*/
static bool is_clang_tmp_label(struct symbol *sym)
{
- return sym->type == STT_NOTYPE &&
+ return is_notype_sym(sym) &&
is_text_sec(sym->sec) &&
strstarts(sym->name, ".Ltmp") &&
isdigit(sym->name[5]);
@@ -355,6 +356,46 @@ static bool dont_correlate(struct symbol *sym)
strstarts(sym->name, "__initcall__");
}
+struct process_demangled_name_data {
+ struct symbol *ret;
+ int count;
+};
+
+static void process_demangled_name(struct symbol *sym, void *d)
+{
+ struct process_demangled_name_data *data = d;
+
+ if (sym->twin)
+ return;
+
+ data->count++;
+ data->ret = sym;
+}
+
+/*
+ * When there is no full name match, try match demangled_name. This would
+ * match original foo.llvm.123 to patched foo.llvm.456.
+ *
+ * Note that, in very rare cases, it is possible to have multiple
+ * foo.llvm.<hash> in the same kernel. When this happens, report error and
+ * fail the diff.
+ */
+static int find_global_symbol_by_demangled_name(struct elf *elf, struct symbol *sym,
+ struct symbol **out_sym)
+{
+ struct process_demangled_name_data data = {};
+
+ iterate_global_symbol_by_demangled_name(elf, sym->demangled_name,
+ process_demangled_name,
+ &data);
+ if (data.count > 1) {
+ ERROR("Multiple (%d) correlation candidates for %s", data.count, sym->name);
+ return -1;
+ }
+ *out_sym = data.ret;
+ return 0;
+}
+
/*
* For each symbol in the original kernel, find its corresponding "twin" in the
* patched kernel.
@@ -453,13 +494,60 @@ static int correlate_symbols(struct elfs *e)
continue;
sym2 = find_global_symbol_by_name(e->patched, sym1->name);
+ if (sym2 && !sym2->twin) {
+ sym1->twin = sym2;
+ sym2->twin = sym1;
+ }
+ }
+
+ /*
+ * Correlate globals with demangled_name.
+ * A separate loop is needed because we want to finish all the
+ * full name correlations first.
+ */
+ for_each_sym(e->orig, sym1) {
+ if (sym1->bind == STB_LOCAL || sym1->twin)
+ continue;
+
+ if (find_global_symbol_by_demangled_name(e->patched, sym1, &sym2))
+ return -1;
- if (sym2 && !sym2->twin && !strcmp(sym1->name, sym2->name)) {
+ if (sym2 && !sym2->twin) {
sym1->twin = sym2;
sym2->twin = sym1;
}
}
+ /* Correlate original locals with patched globals */
+ for_each_sym(e->orig, sym1) {
+ if (sym1->twin || dont_correlate(sym1) || !is_local_sym(sym1))
+ continue;
+
+ sym2 = find_global_symbol_by_name(e->patched, sym1->name);
+ if (!sym2 && find_global_symbol_by_demangled_name(e->patched, sym1, &sym2))
+ return -1;
+
+ if (sym2 && !sym2->twin) {
+ sym1->twin = sym2;
+ sym2->twin = sym1;
+ }
+ }
+
+ /* Correlate original globals with patched locals */
+ for_each_sym(e->patched, sym2) {
+ if (sym2->twin || dont_correlate(sym2) || !is_local_sym(sym2))
+ continue;
+
+ sym1 = find_global_symbol_by_name(e->orig, sym2->name);
+ if (!sym1 && find_global_symbol_by_demangled_name(e->orig, sym2, &sym1))
+ return -1;
+
+ if (sym1 && !sym1->twin) {
+ sym2->twin = sym1;
+ sym1->twin = sym2;
+ }
+ }
+
for_each_sym(e->orig, sym1) {
if (sym1->twin || dont_correlate(sym1))
continue;
@@ -480,7 +568,7 @@ static unsigned long find_sympos(struct elf *elf, struct symbol *sym)
if (sym->bind != STB_LOCAL)
return 0;
- if (vmlinux && sym->type == STT_FUNC) {
+ if (vmlinux && is_func_sym(sym)) {
/*
* HACK: Unfortunately, symbol ordering can differ between
* vmlinux.o and vmlinux due to the linker script emitting
@@ -560,7 +648,7 @@ static struct symbol *__clone_symbol(struct elf *elf, struct symbol *patched_sym
}
if (!is_sec_sym(patched_sym))
- offset = sec_size(out_sec);
+ offset = ALIGN(sec_size(out_sec), out_sec->sh.sh_addralign);
if (patched_sym->len || is_sec_sym(patched_sym)) {
void *data = NULL;
@@ -1046,8 +1134,8 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
sec->name, offset, patched_sym->name, \
addend >= 0 ? "+" : "-", labs(addend), \
sym_type(patched_sym), \
- patched_sym->type == STT_SECTION ? "" : " ", \
- patched_sym->type == STT_SECTION ? "" : sym_bind(patched_sym), \
+ is_sec_sym(patched_sym) ? "" : " ", \
+ is_sec_sym(patched_sym) ? "" : sym_bind(patched_sym), \
is_undef_sym(patched_sym) ? " UNDEF" : "", \
export ? " EXPORTED" : "", \
klp ? " KLP" : "")
@@ -1334,25 +1422,25 @@ static bool should_keep_special_sym(struct elf *elf, struct symbol *sym)
* be applied after static branch/call init, resulting in code corruption.
*
* Validate a special section entry to avoid that. Note that an inert
- * tracepoint is harmless enough, in that case just skip the entry and print a
- * warning. Otherwise, return an error.
+ * tracepoint or pr_debug() is harmless enough, in that case just skip the
+ * entry and print a warning. Otherwise, return an error.
*
- * This is only a temporary limitation which will be fixed when livepatch adds
- * support for submodules: fully self-contained modules which are embedded in
- * the top-level livepatch module's data and which can be loaded on demand when
- * their corresponding to-be-patched module gets loaded. Then klp relocs can
- * be retired.
+ * TODO: This is only a temporary limitation which will be fixed when livepatch
+ * adds support for submodules: fully self-contained modules which are embedded
+ * in the top-level livepatch module's data and which can be loaded on demand
+ * when their corresponding to-be-patched module gets loaded. Then klp relocs
+ * can be retired.
*
* Return:
* -1: error: validation failed
- * 1: warning: tracepoint skipped
+ * 1: warning: disabled tracepoint or pr_debug()
* 0: success
*/
static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym)
{
bool static_branch = !strcmp(sym->sec->name, "__jump_table");
bool static_call = !strcmp(sym->sec->name, ".static_call_sites");
- struct symbol *code_sym = NULL;
+ const char *code_sym = NULL;
unsigned long code_offset = 0;
struct reloc *reloc;
int ret = 0;
@@ -1364,12 +1452,15 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
const char *sym_modname;
struct export *export;
+ if (convert_reloc_sym(e->patched, reloc))
+ continue;
+
/* Static branch/call keys are always STT_OBJECT */
if (reloc->sym->type != STT_OBJECT) {
/* Save code location which can be printed below */
if (reloc->sym->type == STT_FUNC && !code_sym) {
- code_sym = reloc->sym;
+ code_sym = reloc->sym->name;
code_offset = reloc_addend(reloc);
}
@@ -1392,16 +1483,26 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
if (!strcmp(sym_modname, "vmlinux"))
continue;
+ if (!code_sym)
+ code_sym = "<unknown>";
+
if (static_branch) {
if (strstarts(reloc->sym->name, "__tracepoint_")) {
WARN("%s: disabling unsupported tracepoint %s",
- code_sym->name, reloc->sym->name + 13);
+ code_sym, reloc->sym->name + 13);
+ ret = 1;
+ continue;
+ }
+
+ if (strstr(reloc->sym->name, "__UNIQUE_ID_ddebug_")) {
+ WARN("%s: disabling unsupported pr_debug()",
+ code_sym);
ret = 1;
continue;
}
ERROR("%s+0x%lx: unsupported static branch key %s. Use static_key_enabled() instead",
- code_sym->name, code_offset, reloc->sym->name);
+ code_sym, code_offset, reloc->sym->name);
return -1;
}
@@ -1412,7 +1513,7 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
}
ERROR("%s()+0x%lx: unsupported static call key %s. Use KLP_STATIC_CALL() instead",
- code_sym->name, code_offset, reloc->sym->name);
+ code_sym, code_offset, reloc->sym->name);
return -1;
}