Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool: Disassemble code with libopcodes instead of running objdump

objtool executes the objdump command to disassemble code. Use libopcodes
instead to have more control about the disassembly scope and output.
If libopcodes is not present then objtool is built without disassembly
support.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@kernel.org>
Link: https://patch.msgid.link/20251121095340.464045-4-alexandre.chartre@oracle.com

authored by

Alexandre Chartre and committed by
Peter Zijlstra
59953303 1013f2e3

+238 -72
+2
tools/objtool/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 arch/x86/lib/inat-tables.c 3 3 /objtool 4 + feature 5 + FEATURE-DUMP.objtool 4 6 fixdep 5 7 libsubcmd/
+2 -1
tools/objtool/Build
··· 7 7 objtool-y += builtin-check.o 8 8 objtool-y += elf.o 9 9 objtool-y += objtool.o 10 - objtool-y += disas.o 10 + 11 + objtool-$(BUILD_DISAS) += disas.o 11 12 12 13 objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o 13 14 objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
+25
tools/objtool/Makefile
··· 70 70 # Always want host compilation. 71 71 HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" 72 72 73 + # 74 + # To support disassembly, objtool needs libopcodes which is provided 75 + # with libbdf (binutils-dev or binutils-devel package). 76 + # 77 + FEATURE_USER = .objtool 78 + FEATURE_TESTS = libbfd disassembler-init-styled 79 + FEATURE_DISPLAY = 80 + include $(srctree)/tools/build/Makefile.feature 81 + 82 + ifeq ($(feature-disassembler-init-styled), 1) 83 + OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED 84 + endif 85 + 86 + BUILD_DISAS := n 87 + 88 + ifeq ($(feature-libbfd),1) 89 + BUILD_DISAS := y 90 + OBJTOOL_CFLAGS += -DDISAS 91 + OBJTOOL_LDFLAGS += -lopcodes 92 + endif 93 + 94 + export BUILD_DISAS 95 + 73 96 AWK = awk 74 97 MKDIR = mkdir 75 98 ··· 126 103 $(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL) 127 104 $(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete 128 105 $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep 106 + $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool 107 + $(Q)$(RM) -r -- $(OUTPUT)feature 129 108 130 109 FORCE: 131 110
+12
tools/objtool/arch/loongarch/decode.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 #include <string.h> 3 3 #include <objtool/check.h> 4 + #include <objtool/disas.h> 4 5 #include <objtool/warn.h> 5 6 #include <asm/inst.h> 6 7 #include <asm/orc_types.h> ··· 415 414 return reloc->sym->offset + reloc_addend(reloc); 416 415 } 417 416 } 417 + 418 + #ifdef DISAS 419 + 420 + int arch_disas_info_init(struct disassemble_info *dinfo) 421 + { 422 + return disas_info_init(dinfo, bfd_arch_loongarch, 423 + bfd_mach_loongarch32, bfd_mach_loongarch64, 424 + NULL); 425 + } 426 + 427 + #endif /* DISAS */
+12
tools/objtool/arch/powerpc/decode.c
··· 3 3 #include <stdio.h> 4 4 #include <stdlib.h> 5 5 #include <objtool/check.h> 6 + #include <objtool/disas.h> 6 7 #include <objtool/elf.h> 7 8 #include <objtool/arch.h> 8 9 #include <objtool/warn.h> ··· 128 127 return 8; 129 128 } 130 129 } 130 + 131 + #ifdef DISAS 132 + 133 + int arch_disas_info_init(struct disassemble_info *dinfo) 134 + { 135 + return disas_info_init(dinfo, bfd_arch_powerpc, 136 + bfd_mach_ppc, bfd_mach_ppc64, 137 + NULL); 138 + } 139 + 140 + #endif /* DISAS */
+12
tools/objtool/arch/x86/decode.c
··· 16 16 17 17 #include <asm/orc_types.h> 18 18 #include <objtool/check.h> 19 + #include <objtool/disas.h> 19 20 #include <objtool/elf.h> 20 21 #include <objtool/arch.h> 21 22 #include <objtool/warn.h> ··· 950 949 return false; 951 950 } 952 951 } 952 + 953 + #ifdef DISAS 954 + 955 + int arch_disas_info_init(struct disassemble_info *dinfo) 956 + { 957 + return disas_info_init(dinfo, bfd_arch_i386, 958 + bfd_mach_i386_i386, bfd_mach_x86_64, 959 + "att"); 960 + } 961 + 962 + #endif /* DISAS */
+9 -5
tools/objtool/check.c
··· 4926 4926 goto out; 4927 4927 } 4928 4928 4929 - free_insns(file); 4930 - 4931 4929 if (opts.stats) { 4932 4930 printf("nr_insns_visited: %ld\n", nr_insns_visited); 4933 4931 printf("nr_cfi: %ld\n", nr_cfi); ··· 4934 4936 } 4935 4937 4936 4938 out: 4937 - if (!ret && !warnings) 4939 + if (!ret && !warnings) { 4940 + free_insns(file); 4938 4941 return 0; 4942 + } 4939 4943 4940 4944 if (opts.werror && warnings) 4941 4945 ret = 1; ··· 4946 4946 if (opts.werror && warnings) 4947 4947 WARN("%d warning(s) upgraded to errors", warnings); 4948 4948 disas_ctx = disas_context_create(file); 4949 - disas_warned_funcs(disas_ctx); 4950 - disas_context_destroy(disas_ctx); 4949 + if (disas_ctx) { 4950 + disas_warned_funcs(disas_ctx); 4951 + disas_context_destroy(disas_ctx); 4952 + } 4951 4953 } 4954 + 4955 + free_insns(file); 4952 4956 4953 4957 if (opts.backup && make_backup()) 4954 4958 return 1;
+121 -66
tools/objtool/disas.c
··· 4 4 */ 5 5 6 6 #include <objtool/arch.h> 7 + #include <objtool/check.h> 7 8 #include <objtool/disas.h> 8 9 #include <objtool/warn.h> 9 10 11 + #include <bfd.h> 10 12 #include <linux/string.h> 13 + #include <tools/dis-asm-compat.h> 11 14 12 15 struct disas_context { 13 16 struct objtool_file *file; 17 + disassembler_ftype disassembler; 18 + struct disassemble_info info; 14 19 }; 20 + 21 + #define DINFO_FPRINTF(dinfo, ...) \ 22 + ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) 23 + 24 + /* 25 + * Initialize disassemble info arch, mach (32 or 64-bit) and options. 26 + */ 27 + int disas_info_init(struct disassemble_info *dinfo, 28 + int arch, int mach32, int mach64, 29 + const char *options) 30 + { 31 + struct disas_context *dctx = dinfo->application_data; 32 + struct objtool_file *file = dctx->file; 33 + 34 + dinfo->arch = arch; 35 + 36 + switch (file->elf->ehdr.e_ident[EI_CLASS]) { 37 + case ELFCLASS32: 38 + dinfo->mach = mach32; 39 + break; 40 + case ELFCLASS64: 41 + dinfo->mach = mach64; 42 + break; 43 + default: 44 + return -1; 45 + } 46 + 47 + dinfo->disassembler_options = options; 48 + 49 + return 0; 50 + } 15 51 16 52 struct disas_context *disas_context_create(struct objtool_file *file) 17 53 { 18 54 struct disas_context *dctx; 55 + struct disassemble_info *dinfo; 56 + int err; 19 57 20 58 dctx = malloc(sizeof(*dctx)); 21 59 if (!dctx) { ··· 62 24 } 63 25 64 26 dctx->file = file; 27 + dinfo = &dctx->info; 28 + 29 + init_disassemble_info_compat(dinfo, stdout, 30 + (fprintf_ftype)fprintf, 31 + fprintf_styled); 32 + 33 + dinfo->read_memory_func = buffer_read_memory; 34 + dinfo->application_data = dctx; 35 + 36 + /* 37 + * bfd_openr() is not used to avoid doing ELF data processing 38 + * and caching that has already being done. Here, we just need 39 + * to identify the target file so we call an arch specific 40 + * function to fill some disassemble info (arch, mach). 41 + */ 42 + 43 + dinfo->arch = bfd_arch_unknown; 44 + dinfo->mach = 0; 45 + 46 + err = arch_disas_info_init(dinfo); 47 + if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { 48 + WARN("failed to init disassembly arch"); 49 + goto error; 50 + } 51 + 52 + dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? 53 + BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; 54 + 55 + disassemble_init_for_target(dinfo); 56 + 57 + dctx->disassembler = disassembler(dinfo->arch, 58 + dinfo->endian == BFD_ENDIAN_BIG, 59 + dinfo->mach, NULL); 60 + if (!dctx->disassembler) { 61 + WARN("failed to create disassembler function"); 62 + goto error; 63 + } 65 64 66 65 return dctx; 66 + 67 + error: 68 + free(dctx); 69 + return NULL; 67 70 } 68 71 69 72 void disas_context_destroy(struct disas_context *dctx) ··· 112 33 free(dctx); 113 34 } 114 35 115 - /* 'funcs' is a space-separated list of function names */ 116 - static void disas_funcs(const char *funcs) 36 + /* 37 + * Disassemble a single instruction. Return the size of the instruction. 38 + */ 39 + static size_t disas_insn(struct disas_context *dctx, 40 + struct instruction *insn) 117 41 { 118 - const char *objdump_str, *cross_compile; 119 - int size, ret; 120 - char *cmd; 42 + disassembler_ftype disasm = dctx->disassembler; 43 + struct disassemble_info *dinfo = &dctx->info; 121 44 122 - cross_compile = getenv("CROSS_COMPILE"); 123 - if (!cross_compile) 124 - cross_compile = ""; 125 - 126 - objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '" 127 - "BEGIN { split(_funcs, funcs); }" 128 - "/^$/ { func_match = 0; }" 129 - "/<.*>:/ { " 130 - "f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);" 131 - "for (i in funcs) {" 132 - "if (funcs[i] == f) {" 133 - "func_match = 1;" 134 - "base = strtonum(\"0x\" $1);" 135 - "break;" 136 - "}" 137 - "}" 138 - "}" 139 - "{" 140 - "if (func_match) {" 141 - "addr = strtonum(\"0x\" $1);" 142 - "printf(\"%%04x \", addr - base);" 143 - "print;" 144 - "}" 145 - "}' 1>&2"; 146 - 147 - /* fake snprintf() to calculate the size */ 148 - size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1; 149 - if (size <= 0) { 150 - WARN("objdump string size calculation failed"); 151 - return; 45 + if (insn->type == INSN_NOP) { 46 + DINFO_FPRINTF(dinfo, "nop%d", insn->len); 47 + return insn->len; 152 48 } 153 49 154 - cmd = malloc(size); 50 + /* 51 + * Set the disassembler buffer to read data from the section 52 + * containing the instruction to disassemble. 53 + */ 54 + dinfo->buffer = insn->sec->data->d_buf; 55 + dinfo->buffer_vma = 0; 56 + dinfo->buffer_length = insn->sec->sh.sh_size; 155 57 156 - /* real snprintf() */ 157 - snprintf(cmd, size, objdump_str, cross_compile, objname, funcs); 158 - ret = system(cmd); 159 - if (ret) { 160 - WARN("disassembly failed: %d", ret); 161 - return; 162 - } 58 + return disasm(insn->offset, &dctx->info); 163 59 } 164 60 61 + /* 62 + * Disassemble a function. 63 + */ 64 + static void disas_func(struct disas_context *dctx, struct symbol *func) 65 + { 66 + struct instruction *insn; 67 + size_t addr; 68 + 69 + printf("%s:\n", func->name); 70 + sym_for_each_insn(dctx->file, func, insn) { 71 + addr = insn->offset; 72 + printf(" %6lx: %s+0x%-6lx ", 73 + addr, func->name, addr - func->offset); 74 + disas_insn(dctx, insn); 75 + printf("\n"); 76 + } 77 + printf("\n"); 78 + } 79 + 80 + /* 81 + * Disassemble all warned functions. 82 + */ 165 83 void disas_warned_funcs(struct disas_context *dctx) 166 84 { 167 85 struct symbol *sym; 168 - char *funcs = NULL, *tmp; 169 86 170 87 if (!dctx) 171 88 return; 172 89 173 90 for_each_sym(dctx->file->elf, sym) { 174 - if (sym->warned) { 175 - if (!funcs) { 176 - funcs = malloc(strlen(sym->name) + 1); 177 - if (!funcs) { 178 - ERROR_GLIBC("malloc"); 179 - return; 180 - } 181 - strcpy(funcs, sym->name); 182 - } else { 183 - tmp = malloc(strlen(funcs) + strlen(sym->name) + 2); 184 - if (!tmp) { 185 - ERROR_GLIBC("malloc"); 186 - return; 187 - } 188 - sprintf(tmp, "%s %s", funcs, sym->name); 189 - free(funcs); 190 - funcs = tmp; 191 - } 192 - } 91 + if (sym->warned) 92 + disas_func(dctx, sym); 193 93 } 194 - 195 - if (funcs) 196 - disas_funcs(funcs); 197 94 }
+9
tools/objtool/include/objtool/arch.h
··· 103 103 unsigned int arch_reloc_size(struct reloc *reloc); 104 104 unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table); 105 105 106 + #ifdef DISAS 107 + 108 + #include <bfd.h> 109 + #include <dis-asm.h> 110 + 111 + int arch_disas_info_init(struct disassemble_info *dinfo); 112 + 113 + #endif /* DISAS */ 114 + 106 115 #endif /* _ARCH_H */
+5
tools/objtool/include/objtool/check.h
··· 127 127 insn && insn->sec == _sec; \ 128 128 insn = next_insn_same_sec(file, insn)) 129 129 130 + #define sym_for_each_insn(file, sym, insn) \ 131 + for (insn = find_insn(file, sym->sec, sym->offset); \ 132 + insn && insn->offset < sym->offset + sym->len; \ 133 + insn = next_insn_same_sec(file, insn)) 134 + 130 135 #endif /* _CHECK_H */
+29
tools/objtool/include/objtool/disas.h
··· 7 7 #define _DISAS_H 8 8 9 9 struct disas_context; 10 + struct disassemble_info; 11 + 12 + #ifdef DISAS 13 + 10 14 struct disas_context *disas_context_create(struct objtool_file *file); 11 15 void disas_context_destroy(struct disas_context *dctx); 12 16 void disas_warned_funcs(struct disas_context *dctx); 17 + int disas_info_init(struct disassemble_info *dinfo, 18 + int arch, int mach32, int mach64, 19 + const char *options); 20 + 21 + #else /* DISAS */ 22 + 23 + #include <objtool/warn.h> 24 + 25 + static inline struct disas_context *disas_context_create(struct objtool_file *file) 26 + { 27 + WARN("Rebuild with libopcodes for disassembly support"); 28 + return NULL; 29 + } 30 + 31 + static inline void disas_context_destroy(struct disas_context *dctx) {} 32 + static inline void disas_warned_funcs(struct disas_context *dctx) {} 33 + 34 + static inline int disas_info_init(struct disassemble_info *dinfo, 35 + int arch, int mach32, int mach64, 36 + const char *options) 37 + { 38 + return -1; 39 + } 40 + 41 + #endif /* DISAS */ 13 42 14 43 #endif /* _DISAS_H */