in-kernel x86 disassember
Subsystems (5)
cover in-kernel x86 disassember
Hi,
Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)
This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))
Currently, this can disassemble only most popular instructions in-kernel, such as non-SSE, non-MMX, non-AVX, and so on, becuase these extended instructions are rarely used in kernel :) This series supports AT&T syntax, but not fully same as objdump. Still it doesn't decode instruction suffix of operand size (w,d,q etc).
The series is also hosted on a repository on GitHub, you can get the latest version from below public repository.
git://github.com/mhiramat/linux.git
This series adds below features:
- Debugfs disassembler interface for kernel function. You can disassemble running kernel function on-line.
- Panic dump shows disassembly code instead of instruction byte stream. It generates more human-readable report. (I strongly recommend you to add a serial logger if it is enabled :))
- Disassemble command for KDB. 'dis' command is now available.
- User-land disassembly tool.
Thank you,
---
Masami Hiramatsu (16):
x86: Split default64 flag from force64 flag
x86: Change the order of segment prefix macro
x86: Add bogus disassembler support
x86: Show kernel symbol in disassembler
x86: Disassemble x86-64 only instructions
x86: Change asm syntax to AT&T-like one
kdb: Provide original instruction modified by sw breakpoint x86/kprobes: Recover breakpoint instruction if KGDB knows
x86: kernel function disassembly interface x86/disasm: Indicate modified instructions tracing/docs: add explanation about disassembler interface
x86: Merge code dump in show_registers
x86: Disassemble support in register dump
x86: Indicate trapped address and probed address x86/kdb: Add x86 disassembe command tools/bogodis: Add bogus disassembler tool in userspace
Documentation/trace/kprobetrace.txt | 14 + arch/x86/Kconfig.debug | 16 + arch/x86/include/asm/disasm.h | 14 + arch/x86/include/asm/inat.h | 38 ++ arch/x86/include/asm/insn.h | 16 + arch/x86/include/asm/kdebug.h | 1 arch/x86/include/asm/kprobes.h | 2 arch/x86/kernel/dumpstack.c | 146 +++++++++ arch/x86/kernel/dumpstack_32.c | 26 -- arch/x86/kernel/dumpstack_64.c | 25 - arch/x86/kernel/kdebugfs.c | 159 +++++++++ arch/x86/kernel/kgdb.c | 72 ++++ arch/x86/kernel/kprobes.c | 9 + arch/x86/lib/Makefile | 18 + arch/x86/lib/disasm.c | 508 ++++++++++++++++++++++++++++++ arch/x86/lib/insn.c | 2 arch/x86/lib/mnemonic.c | 96 ++++++ arch/x86/tools/gen-insn-attr-x86.awk | 7 arch/x86/tools/gen-insn-mnemonic-x86.awk | 352 +++++++++++++++++++++ include/linux/kdb.h | 3 include/linux/kgdb.h | 1 kernel/debug/debug_core.c | 14 + kernel/debug/kdb/kdb_main.c | 35 ++ tools/bogodis/Makefile | 51 +++ tools/bogodis/bogodis.c | 202 ++++++++++++ 25 files changed, 1759 insertions(+), 68 deletions(-) create mode 100644 arch/x86/include/asm/disasm.h create mode 100644 arch/x86/lib/disasm.c create mode 100644 arch/x86/lib/mnemonic.c create mode 100644 arch/x86/tools/gen-insn-mnemonic-x86.awk create mode 100644 tools/bogodis/Makefile create mode 100644 tools/bogodis/bogodis.c
--
Masami Hiramatsu <[email protected]>
RFC PATCH 1/16 x86: Split default64 flag from force64 flag
Since current x86 instruction decoder treats default 64 bit instructions as same as force 64 bit one, eventually the decoder says the operand size is always 8 on those instructions.
However, in real, when thoes have an operand-size prefix, its operand size should be 2.
So this fixes that to split default64 from force64.
--- arch/x86/include/asm/inat.h | 18 ++++++++++++------ arch/x86/include/asm/insn.h | 2 ++ arch/x86/lib/insn.c | 2 ++ arch/x86/tools/gen-insn-attr-x86.awk | 7 +++++-- 4 files changed, 21 insertions(+), 8 deletions(-)
arch/x86/include/asm/inat.h +12 -6
@@ -83,12 +83,13 @@/* Flags */ #define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS)#define INAT_MODRM (1 << (INAT_FLAG_OFFS)) -#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1)) -#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2)) -#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3)) -#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) -#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) -#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) +#define INAT_DEFAULT64 (1 << (INAT_FLAG_OFFS + 1)) +#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 2)) +#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 3)) +#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 4)) +#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 5)) +#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 6)) +#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 7))/* Attribute making macros for attribute tables */ #define INAT_MAKE_PREFIX (pfx) (pfx << INAT_PFX_OFFS)#define INAT_MAKE_ESCAPE (esc) (esc << INAT_ESC_OFFS)
@@ -194,6 +195,11 @@ static inline int inat_is_force64(insn_attr_t attr)return attr &INAT_FORCE64 ; } +static inline int inat_is_default64 (insn_attr_t attr) +{ +return attr &INAT_DEFAULT64 ; +} +static inline int inat_has_second_immediate (insn_attr_t attr) {return attr &INAT_SCNDIMM ;
arch/x86/include/asm/insn.h +2 -0
@@ -83,6 +83,8 @@ struct insn {#define X86_REX_X (rex) ((rex) & 2)#define X86_REX_B (rex) ((rex) & 1) +#define X86_OPCODE_GPR (opcode) ((opcode) & 0x07) +/* VEX bit flags */ #define X86_VEX_W (vex) ((vex) & 0x80)/* VEX3 Byte2 */ #define X86_VEX_R (vex) ((vex) & 0x80)/* VEX2/3 Byte1 */
arch/x86/lib/insn.c +2 -0
@@ -257,6 +257,8 @@ void insn_get_modrm(struct insn *insn)if (insn->x86_64 &&inat_is_force64 (insn->attr)) insn->opnd_bytes =8 ; +if (insn->x86_64 &&inat_is_default64 (insn->attr) && insn->opnd_bytes ==4 ) + insn->opnd_bytes =8 ; modrm->got =1 ; err_out:
arch/x86/tools/gen-insn-attr-x86.awk +5 -2
@@ -62,7 +62,8 @@ BEGIN { imm_flag["Lx"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)" modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" - force64_expr = "\\([df]64\\)" + force64_expr = "\\(f64\\)" + def64_expr = "\\(d64\\)" rex_expr = "^REX(\\.[XRWB]+)*" fpu_expr = "^ESC" # TODO
@@ -303,9 +304,11 @@ function convert_operands(count,opnd, i,j,imm,mod) } flags = add_flags(flags, "INAT_MAKE_GROUP(" group[opcode] ")") } - # check force(or default) 64bit + # check force or default 64bit if (match(ext, force64_expr)) flags = add_flags(flags, "INAT_FORCE64") + if (match(ext, def64_expr)) + flags = add_flags(flags, "INAT_DEFAULT64") # check REX prefix if (match(opcode, rex_expr))
RFC PATCH 2/16 x86: Change the order of segment prefix macro
In reply to Masami HiramatsuChange the order of segment prefix macros to fit intel's segment register order.'s segment register order.
--- arch/x86/include/asm/inat.h | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-)
arch/x86/include/asm/inat.h +6 -6
@@ -36,12 +36,12 @@#define INAT_PFX_REPNE 3/* 0xF2 */ /* LPFX3 */ /* Other Legacy prefixes */ #define INAT_PFX_LOCK 4/* 0xF0 */ -#define INAT_PFX_CS 5/* 0x2E */ -#define INAT_PFX_DS 6/* 0x3E */ -#define INAT_PFX_ES 7/* 0x26 */ -#define INAT_PFX_FS 8/* 0x64 */ -#define INAT_PFX_GS 9/* 0x65 */ -#define INAT_PFX_SS 10/* 0x36 */ +#define INAT_PFX_ES 5/* 0x26 */ +#define INAT_PFX_CS 6/* 0x2E */ +#define INAT_PFX_SS 7/* 0x36 */ +#define INAT_PFX_DS 8/* 0x3E */ +#define INAT_PFX_FS 9/* 0x64 */ +#define INAT_PFX_GS 10/* 0x65 */ #define INAT_PFX_ADDRSZ 11/* 0x67 */ /* x86-64 REX prefix */ #define INAT_PFX_REX 12/* 0x4X */
RFC PATCH 3/16 x86: Add bogus disassembler support
In reply to Masami HiramatsuAdd a bogus x86 in-kernel disassember to dress up your panic message :)
This time, the disassembler just supports basic instructions (no SSE, no AVX, no FPU etc.). However most of the case, it can show the code in kernel.
Note that this currently shows assembler code in intel format, instead of gas-like att format.
--- arch/x86/Kconfig.debug | 9 + arch/x86/include/asm/disasm.h | 14 + arch/x86/include/asm/inat.h | 8 arch/x86/include/asm/insn.h | 14 + arch/x86/lib/Makefile | 18 + arch/x86/lib/disasm.c | 530 ++++++++++++++++++++++++++++++ arch/x86/lib/mnemonic.c | 77 ++++ arch/x86/tools/gen-insn-mnemonic-x86.awk | 344 +++++++++++++++++++ 8 files changed, 1009 insertions(+), 5 deletions(-) create mode 100644 arch/x86/include/asm/disasm.h create mode 100644 arch/x86/lib/disasm.c create mode 100644 arch/x86/lib/mnemonic.c create mode 100644 arch/x86/tools/gen-insn-mnemonic-x86.awk
arch/x86/Kconfig.debug +9 -0
@@ -172,6 +172,15 @@ config X86_DECODER_SELFTEST decoder code. If unsure, say "N". +config X86_DISASSEMBLER + bool "Enable x86 instruction disassembler" + depends on INSTRUCTION_DECODER + ---help--- + This option enables x86 instruction disassembler code in kernel. + This will show disassembled code on console when you hit a bug or + kernel panic. + If unsure, say "Y" here, since this will help you to report bugs. + # # IO delay types: #
arch/x86/include/asm/disasm.h new file +14 -0
@@ -0,0 +1,14 @@ +#ifndef __X86_DISASM_H__ +#define __X86_DISASM_H__ +#include <asm/insn.h> + +/* Mnemonic format table lookup routines */ +extern const char *get_mnemonic_format (struct insn *insn,const char **grp); +extern const char *get_prefix_name (struct insn *insn); + +#define DISASM_STR_LEN 128 + +/* Disassemble given decoded instruction */ +extern int disassemble (char *buf,size_t len,struct insn *insn); + +#endif /*__X86_DISASM_H__*/
arch/x86/include/asm/inat.h +8 -0
@@ -50,6 +50,8 @@#define INAT_PFX_VEX3 14/* 3-bytes VEX prefix */ #define INAT_LSTPFX_MAX 3 +#define INAT_SEGPFX_MIN 5 +#define INAT_SEGPFX_MAX 10#define INAT_LGCPFX_MAX 11/* Immediate size */
@@ -116,6 +118,12 @@ static inline int inat_is_legacy_prefix(insn_attr_t attr)return attr && attr <=INAT_LGCPFX_MAX ; } +static inline int inat_is_segment_prefix (insn_attr_t attr) +{ + attr &=INAT_PFX_MASK ; +return INAT_SEGPFX_MIN <= attr && attr <=INAT_SEGPFX_MAX ; +} +static inline int inat_is_address_size_prefix (insn_attr_t attr) {return (attr &INAT_PFX_MASK ) ==INAT_PFX_ADDRSZ ;
arch/x86/include/asm/insn.h +14 -0
@@ -82,6 +82,7 @@ struct insn {#define X86_REX_R (rex) ((rex) & 4)#define X86_REX_X (rex) ((rex) & 2)#define X86_REX_B (rex) ((rex) & 1) +#define X86_REX_WRXB (rex) ((rex) & 0xf)#define X86_OPCODE_GPR (opcode) ((opcode) & 0x07)
@@ -168,6 +169,19 @@ static inline int insn_last_prefix_id(struct insn *insn)return 0 ; } +static inline insn_attr_t insn_has_segment_prefix (struct insn *insn) +{ +insn_attr_t attr; +int i; + +for (i =0 ; i <4 ; i++) { + attr =inat_get_opcode_attribute (insn->prefixes.bytes[i]); +if (inat_is_segment_prefix (attr)) +return attr; + } +return 0 ; +} +/* Offset of each field from kaddr */ static inline int insn_offset_rex_prefix (struct insn *insn) {
arch/x86/lib/Makefile +13 -5
@@ -1,18 +1,25 @@ # # Makefile for x86 specific library files. # +x86_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt inat_tables_script = $(srctree)/arch/x86/tools/gen-insn-attr-x86.awk -inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt quiet_cmd_inat_tables = GEN $@ - cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ || rm -f $@ - -$(obj)/inat-tables.c: $(inat_tables_script) $(inat_tables_maps) + cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(x86_tables_maps) > $@ || rm -f $@ +$(obj)/inat-tables.c: $(inat_tables_script) $(x86_tables_maps) $(call cmd,inat_tables) $(obj)/inat.o: $(obj)/inat-tables.c -clean-files := inat-tables.c +mnemonic_tables_script = $(srctree)/arch/x86/tools/gen-insn-mnemonic-x86.awk +quiet_cmd_mnemonic_tables = GEN $@ + cmd_mnemonic_tables = $(AWK) -f $(mnemonic_tables_script) $(x86_tables_maps) > $@ || rm -f $@ +$(obj)/mnemonic-tables.c: $(mnemonic_tables_script) $(x86_tables_maps) + $(call cmd,mnemonic_tables) + +$(obj)/mnemonic.o: $(obj)/mnemonic-tables.c + +clean-files := inat-tables.c mnemonic-tables.c obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o
@@ -23,6 +30,7 @@ lib-y += memcpy_$(BITS).o lib-$(CONFIG_SMP) += rwlock.o lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o +lib-$(CONFIG_X86_DISASSEMBLER) += disasm.o mnemonic.o obj-y += msr.o msr-reg.o msr-reg-export.o
arch/x86/lib/disasm.c new file +530 -0
530 lines not on this page · raw message
arch/x86/lib/mnemonic.c new file +77 -0
77 lines not on this page · raw message
arch/x86/tools/gen-insn-mnemonic-x86.awk new file +344 -0
344 lines not on this page · raw message
RFC PATCH 4/16 x86: Show kernel symbol in disassembler
In reply to Masami HiramatsuShow kernel symbol if the immediate value of disassembling instruction is the destination of jump or call.
--- arch/x86/lib/disasm.c | 25 ++++++++++++++++++++++++- 1 files changed, 24 insertions(+), 1 deletions(-)
arch/x86/lib/disasm.c +24 -1
@@ -7,6 +7,8 @@#include <linux/string.h> #include <linux/ctype.h> #include <linux/errno.h> +#include <linux/kallsyms.h> +#include <asm/disasm.h> #define X86_LEA_OPCODE 0x8d
@@ -28,6 +30,27 @@ static int psnprintf(char **buf, size_t *len, const char *fmt, ...)return ret; } +/* Print address with symbol */ +static int psnprint_symbol (char **buf,size_t *len,unsigned long addr) +{ +unsigned long offs; +char func[KSYM_NAME_LEN ]; +char *modname; +int ret; + + ret =psnprintf (buf, len,"%lx" , addr); +if (!kallsyms_lookup (addr,NULL , &offs, &modname, func)) +return ret; + +psnprintf (buf, len," <%s" , func); +if (offs) +psnprintf (buf, len,"+0x%lx" , offs); +if (modname) +psnprintf (buf, len," [%s]" , modname); + +return psnprintf (buf, len,">" ); +} +/* Operand classifiers */ static bool operand_is_register (const char *p) {
@@ -385,7 +408,7 @@ static int disasm_immediate(char **buf, size_t *len, const char *opnd,if (opnd[0 ] =='J' || opnd[0 ] =='A' ) {if (opnd[0 ] =='J' )/* Relative from IP */ imm += (long )insn->kaddr + insn->length; -return psnprintf (buf, len,"%lx" , (unsigned long )imm); +return psnprint_symbol (buf, len, (unsigned long )imm); } size = insn->opnd_bytes;
RFC PATCH 5/16 x86: Disassemble x86-64 only instructions
In reply to Masami HiramatsuSupport x86-64 only instructions on disassembler.
For example, syscall and sysret.
--- arch/x86/lib/mnemonic.c | 23 +++++++++++++++++++++-- arch/x86/tools/gen-insn-mnemonic-x86.awk | 11 +++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-)
arch/x86/lib/mnemonic.c +21 -2
@@ -1,10 +1,29 @@#include <linux/kernel.h> +#include <linux/string.h> #include <asm/insn.h> #include <asm/disasm.h> /* Define mnemonic lookup table */ #include "mnemonic-tables.c" +static const char *get_variant (const char *fmt,struct insn *insn) +{ +const char *p; + +if (!fmt) + goto out; + +if (insn->x86_64) { + p =strstr (fmt,"%6" ); +if (!p) + goto out; + fmt =strchr (p,':' ) +1 ; + }else if (strstr (fmt,"%6" ) == fmt) + fmt =NULL ; +out: +return fmt; +} +const char *get_mnemonic_format (struct insn *insn,const char **grp) {insn_attr_t attr;
@@ -39,7 +58,7 @@ const char *get_mnemonic_format(struct insn *insn, const char **grp) table = mnemonic_escape_tables[n][0 ]; }if (table) - ret = table[idx]; + ret =get_variant (table[idx], insn);/* Solve groups */ if (grp &&inat_is_group (attr)) {
@@ -51,7 +70,7 @@ const char *get_mnemonic_format(struct insn *insn, const char **grp)else table = mnemonic_group_tables[n][0 ]; idx =X86_MODRM_REG (idx); - *grp = table[idx]; + *grp =get_variant (table[idx], insn); } }return ret;
arch/x86/tools/gen-insn-mnemonic-x86.awk +9 -2
@@ -49,6 +49,7 @@ BEGIN { regs_expr = "^[ABCDESR][0-9A-Z]" vregs_expr = "^[re][0-9A-Z]" + only64_expr = "\\(o64\\)" lprefix1_expr = "\\(66\\)" lprefix2_expr = "\\(F3\\)" lprefix3_expr = "\\(F2\\)"
@@ -254,6 +255,7 @@ function get_operand(opnd, i,count,f8,opnds) { ext = null flags = null opnd = null + pfx = "" # parse one opcode if (match($i, opnd_expr)) opnd = get_operand($(i++))
@@ -283,10 +285,15 @@ function get_operand(opnd, i,count,f8,opnds) { opcode = "call" if (match(opcode, "^ret.*")) opcode = "ret" + + # additional flags + if (match(ext, only64_expr)) + pfx = "%6:" + if (length(opnd) != 0) - flags = "\"" opcode " " opnd "\"" + flags = "\"" pfx opcode " " opnd "\"" else - flags = "\"" opcode "\"" + flags = "\"" pfx opcode "\"" if (length(flags) == 0) continue
RFC PATCH 6/16 x86: Change asm syntax to AT&T-like one
In reply to Masami HiramatsuFor most x86 linux kernel developers, change the asm syntax to AT&T style as like as gas default.
This changes followings;
- Move the last operand to the first
- Remove pointer-size description (e.g. "DWORD PTR")
- Change effective address expression
- Add '%' prefix to registers
- Add '$' prefix to non-address immediates
Note, this doesn't support operand-size suffix of mnemonics as like as "movl" or "popd". Instead, this still shows the registers with width prefixes like as "%eax", "%rdx" etc. Also, this doesn't support '*' prefix which is for the absolute jump address.
--- arch/x86/lib/disasm.c | 130 +++++++++--------------------- arch/x86/tools/gen-insn-mnemonic-x86.awk | 5 + 2 files changed, 41 insertions(+), 94 deletions(-)
arch/x86/lib/disasm.c +38 -92
@@ -125,35 +125,35 @@ static int bad_modrm_operand(char c, int mod)static int psnprint_gpr8 (char **buf,size_t *len,int idx) {if (idx <8 ) -return psnprintf (buf, len,"%s" , gpreg8_map[idx]); +return psnprintf (buf, len,"%%%s" , gpreg8_map[idx]);else if (16 < idx && idx <24 ) -return psnprintf (buf, len,"%s" , gpreg8_map2[idx -16 ]); +return psnprintf (buf, len,"%%%s" , gpreg8_map2[idx -16 ]);else -return psnprintf (buf, len,"r%dl" , idx); +return psnprintf (buf, len,"%%r%dl" , idx); }static int psnprint_gpr16 (char **buf,size_t *len,int idx) {if (idx <8 ) -return psnprintf (buf, len,"%s" , gpreg_map[idx]); +return psnprintf (buf, len,"%%%s" , gpreg_map[idx]);else -return psnprintf (buf, len,"r%dw" , idx); +return psnprintf (buf, len,"%%r%dw" , idx); }static int psnprint_gpr32 (char **buf,size_t *len,int idx) {if (idx <8 ) -return psnprintf (buf, len,"e%s" , gpreg_map[idx]); +return psnprintf (buf, len,"%%e%s" , gpreg_map[idx]);else -return psnprintf (buf, len,"r%dd" , idx); +return psnprintf (buf, len,"%%r%dd" , idx); }static int psnprint_gpr64 (char **buf,size_t *len,int idx) {if (idx <8 ) -return psnprintf (buf, len,"r%s" , gpreg_map[idx]); +return psnprintf (buf, len,"%%r%s" , gpreg_map[idx]);else -return psnprintf (buf, len,"r%d" , idx); +return psnprintf (buf, len,"%%r%d" , idx); }/* Disassemble GPR operands */
@@ -225,57 +225,7 @@ static int __disasm_gprea(char **buf, size_t *len, const char *opnd,else if (insn->addr_bytes ==4 )return psnprint_gpr32 (buf, len, idx);else -return psnprintf (buf, len,"%s" , gprea16_map[idx]); -} - -static int get_operand_size (struct insn *insn,int type) -{ -int size = insn->opnd_bytes; - -switch (type) { -case 'b' : - size =1 ; -break ; -case 'w' : - size =2 ; -break ; -case 'd' : - size =4 ; -break ; -case 'q' : - size =8 ; -break ; -case 'z' : -if (size ==8 ) - size =4 ; -break ; - } -return size; -} - -static int disasm_pointer (char **buf,size_t *len,const char *opnd, -const char *end,struct insn *insn) -{ -const char *type ="(bad)" ; - -if (insn->opcode.bytes[0 ] ==X86_LEA_OPCODE ) -return 0 ; - -switch (get_operand_size (insn, opnd[1 ])) { -case 1 : - type ="BYTE" ; -break ; -case 2 : - type ="WORD" ; -break ; -case 4 : - type ="DWORD" ; -break ; -case 8 : - type ="QWORD" ; -break ; - } -return psnprintf (buf, len,"%s PTR " , type); +return psnprintf (buf, len,"%%%s" , gprea16_map[idx]); }/* Disassemble a segment prefix */
@@ -292,7 +242,7 @@ static int __disasm_segment_prefix(char **buf, size_t *len, } attr = (attr &INAT_PFX_MASK ) -INAT_SEGPFX_MIN ; -return psnprintf (buf, len,"%s:" , segreg_map[attr]); +return psnprintf (buf, len,"%%%s:" , segreg_map[attr]); }static int disasm_segment_prefix (char **buf,size_t *len,struct insn *insn)
@@ -322,23 +272,22 @@ static int disasm_sib(char **buf, size_t *len, const char *opnd,return disasm_displacement (buf, len, insn);disasm_segment_prefix (buf, len, insn); -psnprintf (buf, len,"[" ); +if (mod !=0 || base ==5 ) {/* With displacement offset */ +if (insn->displacement.value <0 ) +psnprintf (buf, len,"-0x%x" , -insn->displacement.value); +else +psnprintf (buf, len,"0x%x" , insn->displacement.value); + } +psnprintf (buf, len,"(" );if (mod !=0 || base !=5 )/* With base */ __disasm_gprea (buf, len, opnd, end, insn, base + rexb);if (index !=4 ) {/* With scale * index */ -if (mod !=0 || base !=5 ) -psnprintf (buf, len,"+" ); +psnprintf (buf, len,"," );__disasm_gprea (buf, len, opnd, end, insn, index + rexx); -psnprintf (buf, len,"*%x" ,1 << scale); - } -if (mod !=0 || base ==5 ) {/* With displacement offset */ -if (insn->displacement.value <0 ) -psnprintf (buf, len,"-0x%x" , -insn->displacement.value); -else -psnprintf (buf, len,"+0x%x" , insn->displacement.value); +psnprintf (buf, len,",%x" ,1 << scale); } -return psnprintf (buf, len,"]" ); +return psnprintf (buf, len,")" ); }/* Disassemble memory-register from MODR/M */
@@ -355,30 +304,28 @@ static int disasm_modrm(char **buf, size_t *len, const char *opnd,return disasm_rm_gpr (buf, len, opnd, end, insn);/* Memory addressing */ -disasm_pointer (buf, len, opnd, end, insn); -if (insn->sib.nbytes)/* SIB addressing */ return disasm_sib (buf, len, opnd, end, insn);if (mod ==0 && rm ==5 ) {/* displacement only */ if (insn_rip_relative (insn))/* RIP relative */ -return psnprintf (buf, len,"[rip+0x%x]" , +return psnprintf (buf, len,"0x%x(%rip)" , insn->displacement.value);else return disasm_displacement (buf, len, insn); }else {disasm_segment_prefix (buf, len, insn); -psnprintf (buf, len,"[" ); -if (insn->rex_prefix.nbytes &&X86_REX_B (insn->rex_prefix.bytes[0 ])) - rm +=8 ; -__disasm_gprea (buf, len, opnd, end, insn, rm);if (mod !=0 ) {if (insn->displacement.value <0 )psnprintf (buf, len,"-0x%x" , -insn->displacement.value);else -psnprintf (buf, len,"+0x%x" , insn->displacement.value); +psnprintf (buf, len,"0x%x" , insn->displacement.value); } -return psnprintf (buf, len,"]" ); +psnprintf (buf, len,"(" ); +if (insn->rex_prefix.nbytes &&X86_REX_B (insn->rex_prefix.bytes[0 ])) + rm +=8 ; +__disasm_gprea (buf, len, opnd, end, insn, rm); +return psnprintf (buf, len,")" ); } }
@@ -416,13 +363,13 @@ static int disasm_immediate(char **buf, size_t *len, const char *opnd, size =1 ;switch (size) {case 8 : -return psnprintf (buf, len,"0x%llx" , imm); +return psnprintf (buf, len,"$0x%llx" , imm);case 4 : -return psnprintf (buf, len,"0x%x" , (unsigned int )imm); +return psnprintf (buf, len,"$0x%x" , (unsigned int )imm);case 2 : -return psnprintf (buf, len,"0x%x" , (unsigned short )imm); +return psnprintf (buf, len,"$0x%x" , (unsigned short )imm);default : -return psnprintf (buf, len,"0x%x" , (unsigned char )imm); +return psnprintf (buf, len,"$0x%x" , (unsigned char )imm); } }
@@ -435,8 +382,7 @@ static int disasm_fixmem(char **buf, size_t *len, const char *opnd,else if (insn->addr_bytes ==8 ) pfx ="r" ; -disasm_pointer (buf, len, opnd, end, insn); -return psnprintf (buf, len,"%cs:[%s%ci]" , *opnd =='x' ?'d' :'e' , +return psnprintf (buf, len,"%%%cs:(%%%s%ci)" , *opnd =='x' ?'d' :'e' , pfx, *opnd =='x' ?'s' :'d' ); }
@@ -452,11 +398,11 @@ static int disasm_register(char **buf, size_t *len, const char *opnd,else if (insn->opnd_bytes ==8 ) pfx[0 ] = opnd[1 ]; opnd +=2 ; -return psnprintf (buf, len,"%s%.*s" , pfx, end - opnd, opnd); +return psnprintf (buf, len,"%%%s%.*s" , pfx, end - opnd, opnd); }else return disasm_opcode_gpr (buf, len, opnd, end, insn); }else -return psnprintf (buf, len,"%.*s" , end - opnd, opnd); +return psnprintf (buf, len,"%%%.*s" , end - opnd, opnd); }/* Disassembe an operand */
@@ -473,13 +419,13 @@ static int disasm_operand(char **buf, size_t *len, const char *opnd,return disasm_reg_gpr (buf, len, opnd, end, insn); else if (operand_is_ctl_reg(opnd)) {int idx =X86_MODRM_REG (insn->modrm.bytes[0 ]); -return psnprintf (buf, len,"cr%d" , idx); +return psnprintf (buf, len,"%%cr%d" , idx); } else if (operand_is_dbg_reg(opnd)) {int idx =X86_MODRM_REG (insn->modrm.bytes[0 ]); -return psnprintf (buf, len,"dr%d" , idx); +return psnprintf (buf, len,"%%dr%d" , idx); } else if (operand_is_seg_reg(opnd)) {int idx =X86_MODRM_REG (insn->modrm.bytes[0 ]); -return psnprintf (buf, len,"%s" , segreg_map[idx]); +return psnprintf (buf, len,"%%%s" , segreg_map[idx]); } else if (operand_is_fixmem(opnd)) return disasm_fixmem(buf, len, opnd, end, insn); else if (operand_is_flags(opnd))
arch/x86/tools/gen-insn-mnemonic-x86.awk +3 -2
@@ -212,10 +212,11 @@ function get_operand(opnd, i,count,f8,opnds) { opnds[i] = "_" tolower(opnds[i]) } - for (i = 1; i <= count; i++) { + opnds[0] = opnds[count] + for (i = 0; i < count; i++) { if (f8 == 1 && match(opnds[i],"Ib")) opnds[i] = toupper(opnds[i]) - if (i == 1) + if (i == 0) opnd = opnds[i] else opnd = opnd "," opnds[i]
RFC PATCH 7/16 kdb: Provide original instruction modified by sw breakpoint
In reply to Masami HiramatsuAdd kdb_get_saved_instr() for someone who needs to know the original instruction in kernel text.
--- include/linux/kgdb.h | 1 + kernel/debug/debug_core.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 0 deletions(-)
include/linux/kgdb.h +1 -0
@@ -209,6 +209,7 @@ extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc);extern int kgdb_validate_break_address (unsigned long addr);extern int kgdb_arch_set_breakpoint (unsigned long addr,char *saved_instr);extern int kgdb_arch_remove_breakpoint (unsigned long addr,char *bundle); +extern int kgdb_get_saved_instr (unsigned long addr,unsigned char *buf);/** * kgdb_arch_late - Perform any architecture specific initalization.
kernel/debug/debug_core.c +14 -0
@@ -349,6 +349,20 @@ int kgdb_isremovedbreak(unsigned long addr)return 0 ; } +int kgdb_get_saved_instr (unsigned long addr,unsigned char *buf) +{ +int i; +for (i =0 ; i <KGDB_MAX_BREAKPOINTS ; i++) { +if (kgdb_break[i].bpt_addr == addr && + kgdb_break[i].state ==BP_ACTIVE ) { +memcpy (buf, kgdb_break[i].saved_instr, +BREAK_INSTR_SIZE ); +return 1 ; + } + } +return 0 ; +} +int dbg_remove_all_break (void ) {unsigned long addr;
RFC PATCH 8/16 x86/kprobes: Recover breakpoint instruction if KGDB knows
In reply to Masami HiramatsuRecover a breakpoint instruction which KGDB has put.
--- arch/x86/kernel/kprobes.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
arch/x86/kernel/kprobes.c +9 -0
@@ -49,6 +49,7 @@#include <linux/kdebug.h> #include <linux/kallsyms.h> #include <linux/ftrace.h> +#include <linux/kgdb.h> #include <asm/cacheflush.h> #include <asm/desc.h>
@@ -252,6 +253,14 @@ unsigned long recover_probed_instruction(kprobe_opcode_t *buf, unsigned long add {unsigned long __addr; +#ifdef CONFIG_KGDB +/* Recover if kgdb has put a breakpoint */ +if (kgdb_get_saved_instr (addr, buf)) { +memcpy (buf +BREAK_INSTR_SIZE , (void *)addr +BREAK_INSTR_SIZE , + (MAX_INSN_SIZE -BREAK_INSTR_SIZE ) *sizeof (kprobe_opcode_t)); +return (unsigned long )buf; + } +#endif __addr =__recover_optprobed_insn (buf, addr);if (__addr != addr)return __addr;
RFC PATCH 9/16 x86: kernel function disassembly interface
In reply to Masami HiramatsuAdd a debugfs interface for disassembling kernel functions as /sys/kernel/debug/x86/disassembly.
To disassemble a kernel function, at first write a symbol name into above file, as below;
# echo sys_symlink > /sys/kernel/debug/x86/disassembly And then, it shows the assembly code of that function. # cat /sys/kernel/debug/x86/disassembly <sys_symlink>: ffffffff81169f90: 55 push %rbp ffffffff81169f91: 48 89 e5 mov %rsp,%rbp ffffffff81169f94: 66 66 66 66 90 nop ffffffff81169f99: 48 89 f2 mov %rsi,%rdx ffffffff81169f9c: be 9c ff ff ff mov $0xffffff9c,%esi ...
--- arch/x86/Kconfig.debug | 7 ++ arch/x86/include/asm/kprobes.h | 2 + arch/x86/kernel/kdebugfs.c | 140 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 0 deletions(-)
arch/x86/Kconfig.debug +7 -0
@@ -181,6 +181,13 @@ config X86_DISASSEMBLER kernel panic. If unsure, say "Y" here, since this will help you to report bugs. +config DEBUG_X86_DISASSEMBLY + bool "x86 instruction disassembly interface on debugfs" + depends on X86_DISASSEMBLER && DEBUG_FS + ---help--- + This option adds /sys/kernel/debug/x86/disassembly interface + for disassembling a kernel function. + # # IO delay types: #
arch/x86/include/asm/kprobes.h +2 -0
@@ -114,4 +114,6 @@ struct kprobe_ctlblk {extern int kprobe_fault_handler (struct pt_regs *regs,int trapnr);extern int kprobe_exceptions_notify (struct notifier_block *self,unsigned long val,void *data); +extern unsigned long recover_probed_instruction (kprobe_opcode_t *buf, +unsigned long addr);#endif /* _ASM_X86_KPROBES_H */
arch/x86/kernel/kdebugfs.c +140 -0
160 lines not on this page · raw message
RFC PATCH 10/16 x86/disasm: Indicate modified instructions
In reply to Masami HiramatsuShow modified comment instead of disassembled code when given instruction has been modified by kprobes or kdb.
--- arch/x86/kernel/kdebugfs.c | 23 +++++++++++++++++++++-- 1 files changed, 21 insertions(+), 2 deletions(-)
arch/x86/kernel/kdebugfs.c +21 -2
@@ -296,13 +296,32 @@ static int disasm_seq_show(struct seq_file *m, void *v)u8 kbuf[MAX_INSN_SIZE ];struct insn insn;void *p; +int i, ret; +/* recover if the instruction is probed */ p = (void *)recover_probed_instruction (kbuf, (unsigned long )v);kernel_insn_init (&insn, p);insn_get_length (&insn); insn.kaddr = v; -snprint_assembly (buf,DISASM_BUF_LEN , &insn,DISASM_PR_ALL ); -seq_printf (m,"%s" , buf); + +seq_printf (m,"%p: " , v); +for (i =0 ; i <MAX_INSN_SIZE /2 && i < insn.length; i++) +seq_printf (m,"%02x " , ((u8 *)v)[i]); +if (i !=MAX_INSN_SIZE /2 ) +seq_printf (m,"%*s" ,3 * (MAX_INSN_SIZE /2 - i)," " ); + +/* print assembly code */ + ret =disassemble (buf,DISASM_BUF_LEN , &insn); +if (ret <0 ) +return ret; +seq_printf (m,"%s%s\n" , (p != v) ?"(probed)" :"" , buf); + +if (i < insn.length) { +seq_printf (m,"%p: " , v + i); +for (; i < insn.length -1 ; i++) +seq_printf (m,"%02x " , ((u8 *)v)[i]); +seq_printf (m,"%02x\n" , ((u8 *)v)[i]); + }return 0 ; }
RFC PATCH 11/16 tracing/docs: add explanation about disassembler interface
In reply to Masami HiramatsuAdd an entry for explaining x86 disassembler interface to kprobetrace.txt, because that is very useful for someone who wants to put a probe on somewhere without using perf-probe.
--- Documentation/trace/kprobetrace.txt | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
Documentation/trace/kprobetrace.txt +14 -0
@@ -92,6 +92,20 @@ Event Profiling the third is the number of probe miss-hits. +Function disassembling +---------------------- + To find a probe target, you have two options. +1) Use perf-probe in perftools. This allows you to find a probe target + by using source-code level information. +2) Use a kernel function disassembling interface on debugfs. +The first one is easy to use, very helpful for noraml user, but also +requires kernel debuginfo package (or built binary with CONFIG_DEBUGINFO). +The second one is easy to use, but requires a knowladge of assembly +language (and also, this is currently available on x86 with +CONFIG_X86_DISASSEMBLER=y and CONFIG_DEBUG_X86_DISASSEMBLY=y). +The debugfs interface is /sys/kernel/debug/x86/disassembly. At first, +you should write a function name to the file, and then, read it. + Usage examples -------------- To add a probe as a new event, write a new definition to kprobe_events
RFC PATCH 12/16 x86: Merge code dump in show_registers
In reply to Masami HiramatsuSince the code dump part of show_registers() in
dumpstack_32/64.c is a dead copy. This should be
merged.
--- arch/x86/include/asm/kdebug.h | 1 + arch/x86/kernel/dumpstack.c | 31 +++++++++++++++++++++++++++++++ arch/x86/kernel/dumpstack_32.c | 26 +------------------------- arch/x86/kernel/dumpstack_64.c | 25 +------------------------ 4 files changed, 34 insertions(+), 49 deletions(-)
arch/x86/include/asm/kdebug.h +1 -0
@@ -24,6 +24,7 @@ enum die_val {extern void printk_address (unsigned long address,int reliable);extern void die (const char *,struct pt_regs *,long );extern int __must_check __die (const char *,struct pt_regs *,long ); +extern void show_code_dump (struct pt_regs *regs);extern void show_registers (struct pt_regs *regs);extern void show_trace (struct task_struct *t,struct pt_regs *regs,unsigned long *sp,unsigned long bp);
arch/x86/kernel/dumpstack.c +31 -0
@@ -292,6 +292,37 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)return 0 ; } +void __kprobes show_code_dump (struct pt_regs *regs) +{ +int i; +unsigned int code_prologue = code_bytes *43 /64 ; +unsigned int code_len = code_bytes; +unsigned char c; +u8 *ip; + + ip = (u8 *)regs->ip - code_prologue; +if (ip < (u8 *)PAGE_OFFSET ||probe_kernel_address (ip, c)) { +/* try starting at IP */ + ip = (u8 *)regs->ip; + code_len = code_len - code_prologue +1 ; + } +for (i =0 ; i < code_len; i++, ip++) { +if (ip < (u8 *)PAGE_OFFSET || +probe_kernel_address (ip, c)) { +#ifdef CONFIG_X86_32 +printk (KERN_CONT " Bad EIP value." ); +#else +printk (KERN_CONT " Bad RIP value." ); +#endif +break ; + } +if (ip == (u8 *)regs->ip) +printk (KERN_CONT "<%02x> " , c); +else +printk (KERN_CONT "%02x " , c); + } +} +/* * This is gone through when something in the kernel has done something bad * and is about to be terminated:
arch/x86/kernel/dumpstack_32.c +1 -25
@@ -84,8 +84,6 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,void show_registers (struct pt_regs *regs) { -int i; -print_modules ();__show_regs (regs, !user_mode_vm (regs));
@@ -97,33 +95,11 @@ void show_registers(struct pt_regs *regs)* time of the fault.. */ if (!user_mode_vm (regs)) { -unsigned int code_prologue = code_bytes *43 /64 ; -unsigned int code_len = code_bytes; -unsigned char c; -u8 *ip; -printk (KERN_EMERG "Stack:\n" );show_stack_log_lvl (NULL , regs, ®s->sp,0 ,KERN_EMERG );printk (KERN_EMERG "Code: " ); - - ip = (u8 *)regs->ip - code_prologue; -if (ip < (u8 *)PAGE_OFFSET ||probe_kernel_address (ip, c)) { -/* try starting at IP */ - ip = (u8 *)regs->ip; - code_len = code_len - code_prologue +1 ; - } -for (i =0 ; i < code_len; i++, ip++) { -if (ip < (u8 *)PAGE_OFFSET || -probe_kernel_address (ip, c)) { -printk (KERN_CONT " Bad EIP value." ); -break ; - } -if (ip == (u8 *)regs->ip) -printk (KERN_CONT "<%02x> " , c); -else -printk (KERN_CONT "%02x " , c); - } +show_code_dump (regs); }printk (KERN_CONT "\n" ); }
arch/x86/kernel/dumpstack_64.c +1 -24
@@ -247,7 +247,6 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,void show_registers (struct pt_regs *regs) { -int i;unsigned long sp;const int cpu =smp_processor_id ();struct task_struct *cur = current;
@@ -264,34 +263,12 @@ void show_registers(struct pt_regs *regs)* time of the fault.. */ if (!user_mode (regs)) { -unsigned int code_prologue = code_bytes *43 /64 ; -unsigned int code_len = code_bytes; -unsigned char c; -u8 *ip; -printk (KERN_DEFAULT "Stack:\n" );show_stack_log_lvl (NULL , regs, (unsigned long *)sp,0 ,KERN_DEFAULT );printk (KERN_DEFAULT "Code: " ); - - ip = (u8 *)regs->ip - code_prologue; -if (ip < (u8 *)PAGE_OFFSET ||probe_kernel_address (ip, c)) { -/* try starting at IP */ - ip = (u8 *)regs->ip; - code_len = code_len - code_prologue +1 ; - } -for (i =0 ; i < code_len; i++, ip++) { -if (ip < (u8 *)PAGE_OFFSET || -probe_kernel_address (ip, c)) { -printk (KERN_CONT " Bad RIP value." ); -break ; - } -if (ip == (u8 *)regs->ip) -printk (KERN_CONT "<%02x> " , c); -else -printk (KERN_CONT "%02x " , c); - } +show_code_dump (regs); }printk (KERN_CONT "\n" ); }
RFC PATCH 13/16 x86: Disassemble support in register dump
In reply to Masami HiramatsuDisassemble executed instructions as same as stackdump when resisters are dumped. The disassemble will replace the code dump and if the code is not in the kernel text, it falls back the classic code dump.
--- arch/x86/kernel/dumpstack.c | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 81 insertions(+), 0 deletions(-)
arch/x86/kernel/dumpstack.c +81 -0
@@ -17,6 +17,8 @@#include <linux/sysfs.h> #include <asm/stacktrace.h> +#include <asm/kprobes.h> +#include <asm/disasm.h> int panic_on_unrecovered_nmi;
@@ -292,6 +294,81 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)return 0 ; } +#ifdef CONFIG_X86_DISASSEMBLER + +/* Find the instruction boundary address */ +static unsigned long find_instruction_boundary (unsigned long saddr, +unsigned long *poffs, +char **modname,char *namebuf) +{ +kprobe_opcode_t buf[MAX_INSN_SIZE ]; +unsigned long offs, addr, fixed; +struct insn insn; + +/* find which function has given ip */ +if (!kallsyms_lookup (saddr,NULL , &offs, modname, namebuf)) +return 0 ; + + addr = saddr - offs;/* Function start address */ +while (addr < saddr) { + fixed =recover_probed_instruction (buf, addr); +kernel_insn_init (&insn, (void *)fixed); +insn_get_length (&insn); + addr += insn.length; + } +if (poffs) + *poffs = offs; + +return addr; +} + +/* Disassemble between (ip - prologue) to (ip - prologue + length) */ +static int disassemble_code_dump (unsigned long ip,unsigned long prologue, +unsigned long length) +{ +kprobe_opcode_t buf[MAX_INSN_SIZE ]; +unsigned long offs, addr, fixed; +unsigned long saddr = ip - prologue; +unsigned long eaddr = ip - prologue + length; +char buf[KSYM_NAME_LEN ] = {0 }; +char *modname; + +/* given address must be in text area */ +if (!kernel_text_address (saddr) || !kernel_text_address (eaddr)) +return -EINVAL ; + + addr =find_instruction_boundary (saddr, &offs, &modname, buf); +if (!addr) +return -EINVAL ; + +if (modname) +printk (KERN_CONT "\n<%s+0x%lx [%s]>:\n" , buf, + addr - (ip - offs), modname); +else +printk (KERN_CONT "\n<%s+0x%lx>:\n" , buf, addr - (ip - offs)); + +do { +if (addr == ip) +printk (KERN_CONT ">>" ); + fixed =recover_probed_instruction (buf, addr); +kernel_insn_init (&insn, (void *)fixed); +insn_get_length (&insn); + insn.kaddr = addr; +snprint_assembly (buf,sizeof (buf), &insn,DISASM_PR_ALL ); +printk (KERN_CONT "%s" , buf); + addr += insn.length; + }while (addr < eaddr); + +return 0 ; +} +#else +static int disassemble_code_dump (unsigned long ip,unsigned long prologue, +unsigned long length) +{ +return -ENOTSUP ; +} +#endif +void __kprobes show_code_dump (struct pt_regs *regs) {int i;
@@ -300,6 +377,10 @@ void __kprobes show_code_dump(struct pt_regs *regs)unsigned char c;u8 *ip; +/* try to disassemble code */ +if (disassemble_code_dump (regs->ip, code_prologue, code_len) ==0 ) +return ; + ip = (u8 *)regs->ip - code_prologue;if (ip < (u8 *)PAGE_OFFSET ||probe_kernel_address (ip, c)) {/* try starting at IP */
RFC PATCH 14/16 x86: Indicate trapped address and probed address
In reply to Masami HiramatsuShow trapped address as a comment style on disassembled code when kernel panicks.
--- arch/x86/kernel/dumpstack.c | 62 +++++++++++++++++++++++++++++++++---------- 1 files changed, 48 insertions(+), 14 deletions(-)
arch/x86/kernel/dumpstack.c +48 -14
@@ -322,22 +322,63 @@ static unsigned long find_instruction_boundary(unsigned long saddr,return addr; } +static int disasm_printk (unsigned long addr,unsigned long *next, +unsigned long ip) +{ +char buf[DISASM_STR_LEN ]; +u8 kbuf[MAX_INSN_SIZE ]; +struct insn insn; +unsigned long fixed; +int i, ret; +u8 *v = (u8 *)addr; + +/* recover if the instruction is probed */ + fixed =recover_probed_instruction (kbuf, addr); +kernel_insn_init (&insn, (void *)fixed); +insn_get_length (&insn); + insn.kaddr = (void *)addr; + +printk (KERN_CONT "%p: " , v); +for (i =0 ; i <MAX_INSN_SIZE /2 && i < insn.length; i++) +printk (KERN_CONT "%02x " , ((u8 *)v)[i]); +if (i !=MAX_INSN_SIZE /2 ) +printk (KERN_CONT "%*s" ,3 * (MAX_INSN_SIZE /2 - i)," " ); + +/* print assembly code */ + ret =disassemble (buf,DISASM_STR_LEN , &insn); +if (ret <0 ) +return ret; +printk (KERN_CONT "%s%s%s\n" , (fixed != addr) ?"(probed)" :"" , buf, + (addr == ip) ?"\t<-- trapping instruction" :"" ); + +if (i < insn.length) { +printk (KERN_CONT "%p: " , v + i); +for (; i < insn.length -1 ; i++) +printk (KERN_CONT "%02x " , ((u8 *)v)[i]); +printk (KERN_CONT "%02x\n" , ((u8 *)v)[i]); + } + +if (next) + *next = addr + insn.length; + +return 0 ; +} +/* Disassemble between (ip - prologue) to (ip - prologue + length) */ static int disassemble_code_dump (unsigned long ip,unsigned long prologue,unsigned long length) { -kprobe_opcode_t buf[MAX_INSN_SIZE ]; -unsigned long offs, addr, fixed; -unsigned long saddr = ip - prologue; +unsigned long offs; +unsigned long addr = ip - prologue;unsigned long eaddr = ip - prologue + length;char buf[KSYM_NAME_LEN ] = {0 };char *modname;/* given address must be in text area */ -if (!kernel_text_address (saddr) || !kernel_text_address (eaddr)) +if (!kernel_text_address (addr) || !kernel_text_address (eaddr))return -EINVAL ; - addr =find_instruction_boundary (saddr, &offs, &modname, buf); + addr =find_instruction_boundary (addr, &offs, &modname, buf);if (!addr)return -EINVAL ;
@@ -348,15 +389,8 @@ static int disassemble_code_dump(unsigned long ip, unsigned long prologue,printk (KERN_CONT "\n<%s+0x%lx>:\n" , buf, addr - (ip - offs));do { -if (addr == ip) -printk (KERN_CONT ">>" ); - fixed =recover_probed_instruction (buf, addr); -kernel_insn_init (&insn, (void *)fixed); -insn_get_length (&insn); - insn.kaddr = addr; -snprint_assembly (buf,sizeof (buf), &insn,DISASM_PR_ALL ); -printk (KERN_CONT "%s" , buf); - addr += insn.length; +if (disasm_printk (addr, &addr, ip) <0 ) +break ; }while (addr < eaddr);return 0 ;
RFC PATCH 15/16 x86/kdb: Add x86 disassembe command
In reply to Masami HiramatsuAdd a simple 'dis' command for x86 on KDB.
This command takes 2 arguments, the first one is the address and the second one is the length of disassembling bytes.
--- arch/x86/kernel/dumpstack.c | 6 ++-- arch/x86/kernel/kgdb.c | 72 +++++++++++++++++++++++++++++++++++++++++++ include/linux/kdb.h | 3 ++ kernel/debug/kdb/kdb_main.c | 35 +++++++++++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-)
arch/x86/kernel/dumpstack.c +3 -3
@@ -297,9 +297,9 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)#ifdef CONFIG_X86_DISASSEMBLER /* Find the instruction boundary address */ -static unsigned long find_instruction_boundary (unsigned long saddr, -unsigned long *poffs, -char **modname,char *namebuf) +unsigned long find_instruction_boundary (unsigned long saddr, +unsigned long *poffs, +char **modname,char *namebuf) {kprobe_opcode_t buf[MAX_INSN_SIZE ];unsigned long offs, addr, fixed;
arch/x86/kernel/kgdb.c +72 -0
88 lines not on this page · raw message
include/linux/kdb.h +3 -0
@@ -166,4 +166,7 @@ enum {extern int kdbgetintenv (const char *,int *);extern int kdb_set (int ,const char **); +/* Some architectures support disassembling in kernel */ +extern int kdb_show_disasm (unsigned long addr,size_t len); +#endif /* !_KDB_H */
kernel/debug/kdb/kdb_main.c +35 -0
@@ -2672,6 +2672,39 @@ static int kdb_per_cpu(int argc, const char **argv)return 0 ; } +int __weak kdb_show_disasm (unsigned long addr,size_t len) +{ +return KDB_NOTIMP ; +} + +/* +* kdb_dis - This function implements the 'dis' command. +*/ +static int kdb_dis (int argc,const char **argv) +{ +int diag; +unsigned long addr; +long offset; +int nextarg; +unsigned long len; + +if (argc >3 ) +return KDB_ARGCOUNT ; + + nextarg =1 ; + diag =kdbgetaddrarg (argc, argv, &nextarg, &addr, &offset,NULL ); +if (diag) +return diag; + +if (argc ==2 ) { +if (kstrtoul (argv[2 ],0 , &len) <0 ) +return KDB_BADINT ; + }else + len =0 ; + +return kdb_show_disasm (addr + offset, (size_t )len); +} +/* * display help for the use of cmd | grep pattern */
@@ -2899,6 +2932,8 @@ static void __init kdb_inittab(void) "Display per_cpu variables", 3, KDB_REPEAT_NONE);kdb_register_repeat ("grephelp" , kdb_grep_help,"" ,"Display help on | grep" ,0 ,KDB_REPEAT_NONE ); +kdb_register_repeat ("dis" , kdb_dis,"<addr> [<len>]" , +"Display disassmbled code" ,2 ,KDB_REPEAT_NONE ); }/* Execute any commands defined in kdb_cmds. */
RFC PATCH 16/16 tools/bogodis: Add bogus disassembler tool in userspace
In reply to Masami HiramatsuThis is a tiny bogus disassembler tool.
Currently, only x86 is supported.
--- arch/x86/lib/disasm.c | 9 ++ tools/bogodis/Makefile | 51 ++++++++++++ tools/bogodis/bogodis.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 0 deletions(-) create mode 100644 tools/bogodis/Makefile create mode 100644 tools/bogodis/bogodis.c
arch/x86/lib/disasm.c +9 -0
@@ -7,7 +7,9 @@#include <linux/string.h> #include <linux/ctype.h> #include <linux/errno.h> +#ifdef __KERNEL__#include <linux/kallsyms.h> +#endif #include <asm/disasm.h>
@@ -30,6 +32,7 @@ static int psnprintf(char **buf, size_t *len, const char *fmt, ...)return ret; } +#ifdef __KERNEL__/* Print address with symbol */ static int psnprint_symbol (char **buf,size_t *len,unsigned long addr) {
@@ -50,6 +53,12 @@ static int psnprint_symbol(char **buf, size_t *len, unsigned long addr) return psnprintf(buf, len, ">"); } +#else +static int psnprint_symbol (char **buf,size_t *len,unsigned long addr) +{ +return psnprintf (buf, len,"%lx" , addr); +} +#endif /* Operand classifiers */ static bool operand_is_register (const char *p)
tools/bogodis/Makefile new file +51 -0
51 lines not on this page · raw message
tools/bogodis/bogodis.c new file +202 -0
202 lines not on this page · raw message
H. Peter Anvin
reviewerQuoted 6 lines · Masami Hiramatsu
This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))
Very day-appropriate ;)
-hpa
Quoted 8 lines · Masami Hiramatsu
Hi,
Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)
Quoted 4 lines
This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))
Nice :-)
Wrt. testing: just wondering, could we eventually attempt to create a user-space testcase for this as well? I.e. if we tried to have a switch to emulate objdump output, we could check that the in-kernel disassembler outputs the same sequence as objdump -d, or so.
[ I realize that this does not cover SSE instructions, which do
sometimes occur in the vmlinux - but 99% of the instruction stream is regular and would be a nice testcase. ]
Quoted 2 lines
- Debugfs disassembler interface for kernel function. You can disassemble running kernel function on-line.
Nice :-)
Quoted 3 lines
- Panic dump shows disassembly code instead of instruction byte stream. It generates more human-readable report. (I strongly recommend you to add a serial logger if it is enabled :))
This is the most useful short-term practical aspect I suspect.
Quoted 2 lines
- Disassemble command for KDB. 'dis' command is now available.
- User-land disassembly tool.
It would be nice to extend the output beyond the boring GNU tooling, for example to auto-label branch targets instead of relying on debuginfo.
This could be used for better visualization as well, instead of the boring and hard to read GNU output:
ffffffff8175d500 <_raw_spin_lock>:
ffffffff8175d500: 55 push %rbp ffffffff8175d501: b8 00 00 01 00 mov $0x10000,%eax ffffffff8175d506: 48 89 e5 mov %rsp,%rbp ffffffff8175d509: f0 0f c1 07 lock xadd %eax,(%rdi) ffffffff8175d50d: 89 c2 mov %eax,%edx ffffffff8175d50f: c1 ea 10 shr $0x10,%edx ffffffff8175d512: 66 39 c2 cmp %ax,%dx ffffffff8175d515: 74 13 je ffffffff8175d52a <_raw_spin_lock+0x2a> ffffffff8175d517: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) ffffffff8175d51e: 00 00 ffffffff8175d520: f3 90 pause ffffffff8175d522: 0f b7 07 movzwl (%rdi),%eax ffffffff8175d525: 66 39 d0 cmp %dx,%ax ffffffff8175d528: 75 f6 jne ffffffff8175d520 <_raw_spin_lock+0x20> ffffffff8175d52a: 5d pop %rbp ffffffff8175d52b: c3 retq ffffffff8175d52c: 0f 1f 40 00 nopl 0x0(%rax)
the default 'human readable' output could be something much more intelligent, like:
<_raw_spin_lock>:
push %rbp
mov $0x10000, %eax
mov %rsp, %rbp
lock xadd %eax, (%rdi)
mov %eax, %edx
shr $0x10, %edx
cmp %ax, %dx
je L2 #-----------------------------.
nop-7 |
|
L1: pause <-------------. |
movzwl (%rdi), %eax | |
cmp %dx, %ax | |
jne L1 #------------------------' |
|L2: pop %rbp <------------------'
retq
This is much more readable, right? Yet it carries all the essential information that the original output one carried.
If vector instructions (SEE, MMX, AVX) are in your list to support then it would be and interesting use to combine this with perf on x86 - which uses objdump right now. Perf could use a programmatic, librarized disassembler for its assembly annotation code.
That would allow new UI features like:
- proper highlighting of jump/branch instructions and navigation along branch instructions (and visualization of possible execution flow) as well.
- register modification and lifetime highlighting. If I click on 'rax' then the output could show how this register gets touched by the code, explicitly and implicitly (a common assembly coding pitfall)
- summarization of usually irrelevant details, like the nop-7 example above.
Another very interesting usecase would be to invert it and create a simpler parser and an in-kernel *assembler*: a GAS replacement in essence. We could build the kernel using its own assembler.
That could also be used for safe sandboxing: the disassembler could be combined with the assembler to ensure that binary code submitted to the kernel is 'safe' to execute - even in kernel-space. A sha1 hash could be used to cache already checked, 'safe' modules of code.
Thanks,
Ingo
H. Peter Anvin
reviewerQuoted 3 lines · Ingo Molnar
Perf could use
a programmatic, librarized disassembler for its assembly annotation code.
BTW, it should be pretty trivial to librarize the disassembler from NASM. Even more so, I think this might have already been done with Yasm.
-hpa
2012/4/3 H. Peter Anvin <[email protected]>:
Quoted 7 lines · Ingo Molnar
Quoted 3 lines
Perf could use
a programmatic, librarized disassembler for its assembly annotation code.BTW, it should be pretty trivial to librarize the disassembler from NASM. Even more so, I think this might have already been done with Yasm.
Technically, yes. There are already many disassemblers. This series was just started for my interesting of how disassember works and helps kprobes debugging :)
Oh, and also, I'd like to try reusing x86-opcode-map.txt which is already in the kernel tree. :)
Thank you,
Signature · 2 lines
Masami Hiramatsu mailto:[email protected]
H. Peter Anvin
reviewerQuoted 8 lines · Masami Hiramatsu
Hi,
Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)
Quoted 5 lines
This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))
This is cool, but I have one major reservation about it: it will make kernel panics take a lot more screen real estate without containing more information, and we already have problems with things scrolling off way too easily.
For that reason I would like to request that this *only* enabled by an explicit command-line option or similar (disasm_oops, maybe?), so that the user has to opt-in. In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.
-hpa
Quoted 15 lines · H. Peter Anvin
Quoted 8 lines · Masami Hiramatsu
Hi,
Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)Quoted 5 lines
This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))
Quoted 4 lines
This is cool, but I have one major reservation about it: it will make kernel panics take a lot more screen real estate without containing more information, and we already have problems with things scrolling off way too easily.
Yes, I'm not sure we want to do it by default.
Quoted 3 lines
For that reason I would like to request that this *only* enabled by an explicit command-line option or similar (
disasm_oops, maybe?), so that the user has to opt-in.
The existing oops setup knob is an early_param() in kernel/panic.c, "oops=".
I'd suggesting extending that in an obvious way. Currently the only option that exists is "oops=panic", so a comma delimited list of attributes would be the natural extension, allowing:
oops=panic
oops=panic,disasm
oops=disasm
Detail: it should do a strncmp(5, str, "disas"), so that every usual variant works: oops=disasm, oops=disassemble, etc.
Quoted 2 lines
[...] In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.
Probably a DEBUG .config option as well, so that distros can enable it. OTOH, CONFIG_CMDLINE allows the setting of such parameters as well.
Thanks,
Ingo
2012/4/3 Ingo Molnar <[email protected]>:
Quoted 18 lines · H. Peter Anvin
Quoted 16 lines · Masami Hiramatsu
Quoted 13 lines
Hi,
Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))
Quoted 6 lines
Quoted 4 lines
This is cool, but I have one major reservation about it: it will make kernel panics take a lot more screen real estate without containing more information, and we already have problems with things scrolling off way too easily.
Yes, I'm not sure we want to do it by default.
Agreed.
Quoted 11 lines
Quoted 3 lines
For that reason I would like to request that this *only* enabled by an explicit command-line option or similar (
disasm_oops, maybe?), so that the user has to opt-in.The existing oops setup knob is an
early_param()inkernel/panic.c, "oops=".I'd suggesting extending that in an obvious way. Currently the only option that exists is "oops=panic", so a comma delimited list of attributes would be the natural extension, allowing:
Quoted 6 lines
oops=panic
oops=panic,disasm
oops=disasmDetail: it should do a
strncmp(5, str, "disas"), so that every usual variant works: oops=disasm, oops=disassemble, etc.
OK, I'll add that.
Quoted 7 lines
Quoted 3 lines
[...] In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.
Probably a DEBUG .config option as well, so that distros can enable it. OTOH,
CONFIG_CMDLINEallows the setting of such parameters as well.
This would be good for other parameters, so I think it should be separately done.
Thank you,
Signature · 2 lines
Masami Hiramatsu mailto:[email protected]
H. Peter Anvin
reviewerQuoted 15 lines · Ingo Molnar
The existing oops setup knob is an
early_param()inkernel/panic.c, "oops=".I'd suggesting extending that in an obvious way. Currently the only option that exists is "oops=panic", so a comma delimited list of attributes would be the natural extension, allowing:
oops=panic
oops=panic,disasm
oops=disasmDetail: it should do a
strncmp(5, str, "disas"), so that every usual variant works: oops=disasm, oops=disassemble, etc.
Quoted 7 lines
Quoted 2 lines
[...] In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.
Probably a DEBUG .config option as well, so that distros can enable it. OTOH,
CONFIG_CMDLINEallows the setting of such parameters as well.
I would say there should be a CONFIG option to build it in, but I'd like to see an explicit command-line option to enable it.
I *do not* want to see this in distro kernels. That is all downside.
-hpa
Signature · 2 lines
H. Peter Anvin, Intel Open Source Technology Center I work for Intel. I don't speak on their behalf.