diff options
| -rw-r--r-- | tools/objtool/.gitignore | 2 | ||||
| -rw-r--r-- | tools/objtool/Build | 3 | ||||
| -rw-r--r-- | tools/objtool/Makefile | 25 | ||||
| -rw-r--r-- | tools/objtool/arch/loongarch/decode.c | 12 | ||||
| -rw-r--r-- | tools/objtool/arch/powerpc/decode.c | 12 | ||||
| -rw-r--r-- | tools/objtool/arch/x86/decode.c | 12 | ||||
| -rw-r--r-- | tools/objtool/check.c | 14 | ||||
| -rw-r--r-- | tools/objtool/disas.c | 187 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/arch.h | 9 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/check.h | 5 | ||||
| -rw-r--r-- | tools/objtool/include/objtool/disas.h | 29 |
11 files changed, 238 insertions, 72 deletions
diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore index 4faa4dd72f35..759303657bd7 100644 --- a/tools/objtool/.gitignore +++ b/tools/objtool/.gitignore @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only arch/x86/lib/inat-tables.c /objtool +feature +FEATURE-DUMP.objtool fixdep libsubcmd/ diff --git a/tools/objtool/Build b/tools/objtool/Build index 17e50a1766d0..9d1e8f28ef95 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -7,7 +7,8 @@ objtool-y += special.o objtool-y += builtin-check.o objtool-y += elf.o objtool-y += objtool.o -objtool-y += disas.o + +objtool-$(BUILD_DISAS) += disas.o objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 021f55b7bd87..df793ca6fc1a 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -70,6 +70,29 @@ OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) # Always want host compilation. HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" +# +# To support disassembly, objtool needs libopcodes which is provided +# with libbdf (binutils-dev or binutils-devel package). +# +FEATURE_USER = .objtool +FEATURE_TESTS = libbfd disassembler-init-styled +FEATURE_DISPLAY = +include $(srctree)/tools/build/Makefile.feature + +ifeq ($(feature-disassembler-init-styled), 1) + OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED +endif + +BUILD_DISAS := n + +ifeq ($(feature-libbfd),1) + BUILD_DISAS := y + OBJTOOL_CFLAGS += -DDISAS + OBJTOOL_LDFLAGS += -lopcodes +endif + +export BUILD_DISAS + AWK = awk MKDIR = mkdir @@ -103,6 +126,8 @@ clean: $(LIBSUBCMD)-clean $(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL) $(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep + $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool + $(Q)$(RM) -r -- $(OUTPUT)feature FORCE: diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index 0115b97c526b..1de86ebb637d 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <string.h> #include <objtool/check.h> +#include <objtool/disas.h> #include <objtool/warn.h> #include <asm/inst.h> #include <asm/orc_types.h> @@ -414,3 +415,14 @@ unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *tabl return reloc->sym->offset + reloc_addend(reloc); } } + +#ifdef DISAS + +int arch_disas_info_init(struct disassemble_info *dinfo) +{ + return disas_info_init(dinfo, bfd_arch_loongarch, + bfd_mach_loongarch32, bfd_mach_loongarch64, + NULL); +} + +#endif /* DISAS */ diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c index 3a9b748216ed..4f68b402e785 100644 --- a/tools/objtool/arch/powerpc/decode.c +++ b/tools/objtool/arch/powerpc/decode.c @@ -3,6 +3,7 @@ #include <stdio.h> #include <stdlib.h> #include <objtool/check.h> +#include <objtool/disas.h> #include <objtool/elf.h> #include <objtool/arch.h> #include <objtool/warn.h> @@ -127,3 +128,14 @@ unsigned int arch_reloc_size(struct reloc *reloc) return 8; } } + +#ifdef DISAS + +int arch_disas_info_init(struct disassemble_info *dinfo) +{ + return disas_info_init(dinfo, bfd_arch_powerpc, + bfd_mach_ppc, bfd_mach_ppc64, + NULL); +} + +#endif /* DISAS */ diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index cc85db7b65a4..83e9c604ce10 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -16,6 +16,7 @@ #include <asm/orc_types.h> #include <objtool/check.h> +#include <objtool/disas.h> #include <objtool/elf.h> #include <objtool/arch.h> #include <objtool/warn.h> @@ -949,3 +950,14 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc) return false; } } + +#ifdef DISAS + +int arch_disas_info_init(struct disassemble_info *dinfo) +{ + return disas_info_init(dinfo, bfd_arch_i386, + bfd_mach_i386_i386, bfd_mach_x86_64, + "att"); +} + +#endif /* DISAS */ diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8b1a6a5185d3..21d45a35f3c9 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4926,8 +4926,6 @@ int check(struct objtool_file *file) goto out; } - free_insns(file); - if (opts.stats) { printf("nr_insns_visited: %ld\n", nr_insns_visited); printf("nr_cfi: %ld\n", nr_cfi); @@ -4936,8 +4934,10 @@ int check(struct objtool_file *file) } out: - if (!ret && !warnings) + if (!ret && !warnings) { + free_insns(file); return 0; + } if (opts.werror && warnings) ret = 1; @@ -4946,10 +4946,14 @@ out: if (opts.werror && warnings) WARN("%d warning(s) upgraded to errors", warnings); disas_ctx = disas_context_create(file); - disas_warned_funcs(disas_ctx); - disas_context_destroy(disas_ctx); + if (disas_ctx) { + disas_warned_funcs(disas_ctx); + disas_context_destroy(disas_ctx); + } } + free_insns(file); + if (opts.backup && make_backup()) return 1; diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c index 7a18e51d43e6..11ac2ec04afc 100644 --- a/tools/objtool/disas.c +++ b/tools/objtool/disas.c @@ -4,18 +4,56 @@ */ #include <objtool/arch.h> +#include <objtool/check.h> #include <objtool/disas.h> #include <objtool/warn.h> +#include <bfd.h> #include <linux/string.h> +#include <tools/dis-asm-compat.h> struct disas_context { struct objtool_file *file; + disassembler_ftype disassembler; + struct disassemble_info info; }; +#define DINFO_FPRINTF(dinfo, ...) \ + ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) + +/* + * Initialize disassemble info arch, mach (32 or 64-bit) and options. + */ +int disas_info_init(struct disassemble_info *dinfo, + int arch, int mach32, int mach64, + const char *options) +{ + struct disas_context *dctx = dinfo->application_data; + struct objtool_file *file = dctx->file; + + dinfo->arch = arch; + + switch (file->elf->ehdr.e_ident[EI_CLASS]) { + case ELFCLASS32: + dinfo->mach = mach32; + break; + case ELFCLASS64: + dinfo->mach = mach64; + break; + default: + return -1; + } + + dinfo->disassembler_options = options; + + return 0; +} + struct disas_context *disas_context_create(struct objtool_file *file) { struct disas_context *dctx; + struct disassemble_info *dinfo; + int err; dctx = malloc(sizeof(*dctx)); if (!dctx) { @@ -24,8 +62,49 @@ struct disas_context *disas_context_create(struct objtool_file *file) } dctx->file = file; + dinfo = &dctx->info; + + init_disassemble_info_compat(dinfo, stdout, + (fprintf_ftype)fprintf, + fprintf_styled); + + dinfo->read_memory_func = buffer_read_memory; + dinfo->application_data = dctx; + + /* + * bfd_openr() is not used to avoid doing ELF data processing + * and caching that has already being done. Here, we just need + * to identify the target file so we call an arch specific + * function to fill some disassemble info (arch, mach). + */ + + dinfo->arch = bfd_arch_unknown; + dinfo->mach = 0; + + err = arch_disas_info_init(dinfo); + if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { + WARN("failed to init disassembly arch"); + goto error; + } + + dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? + BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; + + disassemble_init_for_target(dinfo); + + dctx->disassembler = disassembler(dinfo->arch, + dinfo->endian == BFD_ENDIAN_BIG, + dinfo->mach, NULL); + if (!dctx->disassembler) { + WARN("failed to create disassembler function"); + goto error; + } return dctx; + +error: + free(dctx); + return NULL; } void disas_context_destroy(struct disas_context *dctx) @@ -33,86 +112,62 @@ void disas_context_destroy(struct disas_context *dctx) free(dctx); } -/* 'funcs' is a space-separated list of function names */ -static void disas_funcs(const char *funcs) +/* + * Disassemble a single instruction. Return the size of the instruction. + */ +static size_t disas_insn(struct disas_context *dctx, + struct instruction *insn) { - const char *objdump_str, *cross_compile; - int size, ret; - char *cmd; - - cross_compile = getenv("CROSS_COMPILE"); - if (!cross_compile) - cross_compile = ""; - - objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '" - "BEGIN { split(_funcs, funcs); }" - "/^$/ { func_match = 0; }" - "/<.*>:/ { " - "f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);" - "for (i in funcs) {" - "if (funcs[i] == f) {" - "func_match = 1;" - "base = strtonum(\"0x\" $1);" - "break;" - "}" - "}" - "}" - "{" - "if (func_match) {" - "addr = strtonum(\"0x\" $1);" - "printf(\"%%04x \", addr - base);" - "print;" - "}" - "}' 1>&2"; - - /* fake snprintf() to calculate the size */ - size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1; - if (size <= 0) { - WARN("objdump string size calculation failed"); - return; + disassembler_ftype disasm = dctx->disassembler; + struct disassemble_info *dinfo = &dctx->info; + + if (insn->type == INSN_NOP) { + DINFO_FPRINTF(dinfo, "nop%d", insn->len); + return insn->len; } - cmd = malloc(size); + /* + * Set the disassembler buffer to read data from the section + * containing the instruction to disassemble. + */ + dinfo->buffer = insn->sec->data->d_buf; + dinfo->buffer_vma = 0; + dinfo->buffer_length = insn->sec->sh.sh_size; - /* real snprintf() */ - snprintf(cmd, size, objdump_str, cross_compile, objname, funcs); - ret = system(cmd); - if (ret) { - WARN("disassembly failed: %d", ret); - return; + return disasm(insn->offset, &dctx->info); +} + +/* + * Disassemble a function. + */ +static void disas_func(struct disas_context *dctx, struct symbol *func) +{ + struct instruction *insn; + size_t addr; + + printf("%s:\n", func->name); + sym_for_each_insn(dctx->file, func, insn) { + addr = insn->offset; + printf(" %6lx: %s+0x%-6lx ", + addr, func->name, addr - func->offset); + disas_insn(dctx, insn); + printf("\n"); } + printf("\n"); } +/* + * Disassemble all warned functions. + */ void disas_warned_funcs(struct disas_context *dctx) { struct symbol *sym; - char *funcs = NULL, *tmp; if (!dctx) return; for_each_sym(dctx->file->elf, sym) { - if (sym->warned) { - if (!funcs) { - funcs = malloc(strlen(sym->name) + 1); - if (!funcs) { - ERROR_GLIBC("malloc"); - return; - } - strcpy(funcs, sym->name); - } else { - tmp = malloc(strlen(funcs) + strlen(sym->name) + 2); - if (!tmp) { - ERROR_GLIBC("malloc"); - return; - } - sprintf(tmp, "%s %s", funcs, sym->name); - free(funcs); - funcs = tmp; - } - } + if (sym->warned) + disas_func(dctx, sym); } - - if (funcs) - disas_funcs(funcs); } diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index d89f8b5ec14e..18c0e69ee617 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc); unsigned int arch_reloc_size(struct reloc *reloc); unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table); +#ifdef DISAS + +#include <bfd.h> +#include <dis-asm.h> + +int arch_disas_info_init(struct disassemble_info *dinfo); + +#endif /* DISAS */ + #endif /* _ARCH_H */ diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index d73b0c3ae1ee..674f57466d12 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -127,4 +127,9 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc insn && insn->sec == _sec; \ insn = next_insn_same_sec(file, insn)) +#define sym_for_each_insn(file, sym, insn) \ + for (insn = find_insn(file, sym->sec, sym->offset); \ + insn && insn->offset < sym->offset + sym->len; \ + insn = next_insn_same_sec(file, insn)) + #endif /* _CHECK_H */ diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h index 5c543b69fc61..3ec3ce2e4e6f 100644 --- a/tools/objtool/include/objtool/disas.h +++ b/tools/objtool/include/objtool/disas.h @@ -7,8 +7,37 @@ #define _DISAS_H struct disas_context; +struct disassemble_info; + +#ifdef DISAS + struct disas_context *disas_context_create(struct objtool_file *file); void disas_context_destroy(struct disas_context *dctx); void disas_warned_funcs(struct disas_context *dctx); +int disas_info_init(struct disassemble_info *dinfo, + int arch, int mach32, int mach64, + const char *options); + +#else /* DISAS */ + +#include <objtool/warn.h> + +static inline struct disas_context *disas_context_create(struct objtool_file *file) +{ + WARN("Rebuild with libopcodes for disassembly support"); + return NULL; +} + +static inline void disas_context_destroy(struct disas_context *dctx) {} +static inline void disas_warned_funcs(struct disas_context *dctx) {} + +static inline int disas_info_init(struct disassemble_info *dinfo, + int arch, int mach32, int mach64, + const char *options) +{ + return -1; +} + +#endif /* DISAS */ #endif /* _DISAS_H */ |
