All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH -tip 00/16] in-kernel x86 disassember
@ 2012-04-01 16:02 Masami Hiramatsu
  2012-04-01 16:02 ` [RFC PATCH -tip 01/16] x86: Split default64 flag from force64 flag Masami Hiramatsu
                   ` (18 more replies)
  0 siblings, 19 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:02 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

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 <masami.hiramatsu@gmail.com>
 

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 01/16] x86: Split default64 flag from force64 flag
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
@ 2012-04-01 16:02 ` Masami Hiramatsu
  2012-04-01 16:02 ` [RFC PATCH -tip 02/16] x86: Change the order of segment prefix macro Masami Hiramatsu
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:02 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

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.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 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(-)

diff --git a/arch/x86/include/asm/inat.h b/arch/x86/include/asm/inat.h
index 74a2e31..55e438b 100644
--- a/arch/x86/include/asm/inat.h
+++ b/arch/x86/include/asm/inat.h
@@ -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;
diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h
index 48eb30a..34aecec 100644
--- a/arch/x86/include/asm/insn.h
+++ b/arch/x86/include/asm/insn.h
@@ -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 */
diff --git a/arch/x86/lib/insn.c b/arch/x86/lib/insn.c
index 25feb1a..e34829a 100644
--- a/arch/x86/lib/insn.c
+++ b/arch/x86/lib/insn.c
@@ -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:
diff --git a/arch/x86/tools/gen-insn-attr-x86.awk b/arch/x86/tools/gen-insn-attr-x86.awk
index 5f6a5b6..76d8889 100644
--- a/arch/x86/tools/gen-insn-attr-x86.awk
+++ b/arch/x86/tools/gen-insn-attr-x86.awk
@@ -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))


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 02/16] x86: Change the order of segment prefix macro
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
  2012-04-01 16:02 ` [RFC PATCH -tip 01/16] x86: Split default64 flag from force64 flag Masami Hiramatsu
@ 2012-04-01 16:02 ` Masami Hiramatsu
  2012-04-01 16:02 ` [RFC PATCH -tip 03/16] x86: Add bogus disassembler support Masami Hiramatsu
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:02 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Change the order of segment prefix macros to fit
intel's segment register order.'s segment register order.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/include/asm/inat.h |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/inat.h b/arch/x86/include/asm/inat.h
index 55e438b..2a86c26 100644
--- a/arch/x86/include/asm/inat.h
+++ b/arch/x86/include/asm/inat.h
@@ -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 */


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 03/16] x86: Add bogus disassembler support
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
  2012-04-01 16:02 ` [RFC PATCH -tip 01/16] x86: Split default64 flag from force64 flag Masami Hiramatsu
  2012-04-01 16:02 ` [RFC PATCH -tip 02/16] x86: Change the order of segment prefix macro Masami Hiramatsu
@ 2012-04-01 16:02 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 04/16] x86: Show kernel symbol in disassembler Masami Hiramatsu
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:02 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Add 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.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 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

diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index e46c214..ae64888 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -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:
 #
diff --git a/arch/x86/include/asm/disasm.h b/arch/x86/include/asm/disasm.h
new file mode 100644
index 0000000..ec5208f
--- /dev/null
+++ b/arch/x86/include/asm/disasm.h
@@ -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__*/
diff --git a/arch/x86/include/asm/inat.h b/arch/x86/include/asm/inat.h
index 2a86c26..2c90f5f 100644
--- a/arch/x86/include/asm/inat.h
+++ b/arch/x86/include/asm/inat.h
@@ -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;
diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h
index 34aecec..d96fca9 100644
--- a/arch/x86/include/asm/insn.h
+++ b/arch/x86/include/asm/insn.h
@@ -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)
 {
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index b00f678..2571061 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -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
 
diff --git a/arch/x86/lib/disasm.c b/arch/x86/lib/disasm.c
new file mode 100644
index 0000000..473ae52
--- /dev/null
+++ b/arch/x86/lib/disasm.c
@@ -0,0 +1,530 @@
+/*
+ * Disasm.c -- the core of bogus disassembler code
+ * Written by Masami Hiramatsu <masami.hiramatsu@gmail.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <asm/disasm.h>
+
+#define X86_LEA_OPCODE 0x8d
+
+static int psnprintf(char **buf, size_t *len, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = vsnprintf(*buf, *len, fmt, ap);
+	va_end(ap);
+	if (ret > 0 && ret < *len) {
+		*buf += ret;
+		*len -= ret;
+	} else
+		ret = -E2BIG;
+
+	return ret;
+}
+
+/* Operand classifiers */
+static bool operand_is_register(const char *p)
+{
+	return !isupper(*p);
+}
+
+static bool operand_is_imm(const char *p)
+{
+	return strchr("AIJO", *p) != NULL;
+}
+
+static bool operand_is_gp_reg(const char *p)
+{
+	return *p == 'G';
+}
+
+static bool operand_is_ctl_reg(const char *p)
+{
+	return *p == 'C';
+}
+
+static bool operand_is_dbg_reg(const char *p)
+{
+	return *p == 'D';
+}
+
+static bool operand_is_seg_reg(const char *p)
+{
+	return *p == 'S';
+}
+
+static bool operand_is_flags(const char *p)
+{
+	return *p == 'F';
+}
+
+static bool operand_is_fixmem(const char *p)
+{
+	return *p == 'X' || *p == 'Y';
+}
+
+static bool operand_is_memreg(const char *p)
+{
+	return *p == 'E' || *p == 'M' || *p == 'R';
+}
+
+/* register maps */
+const char *gpreg_map[8] = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
+const char *gpreg8_map[8] = {"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"};
+const char *gpreg8_map2[8] = {"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil"};
+const char *segreg_map[8] = {"es", "cs", "ss", "ds", "fs", "gs", "(bad)", "(bad)"};
+const char *gprea16_map[8] = {"bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"};
+
+static unsigned int insn_field_get_uval(struct insn_field *field)
+{
+	switch (field->nbytes) {
+	case 1:
+		return field->bytes[0];
+	case 2:
+		return (unsigned short)field->value;
+	default:
+		return (unsigned int)field->value;
+	}
+}
+
+static int bad_modrm_operand(char c, int mod)
+{
+	return (c == 'R' && mod != 3) || (c == 'M' && mod == 3);
+}
+
+/* Print General Purpose Registers by number */
+static int psnprint_gpr8(char **buf, size_t *len, int idx)
+{
+	if (idx < 8)
+		return psnprintf(buf, len, "%s", gpreg8_map[idx]);
+	else if (16 < idx && idx < 24)
+		return psnprintf(buf, len, "%s", gpreg8_map2[idx - 16]);
+	else
+		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]);
+	else
+		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]);
+	else
+		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]);
+	else
+		return psnprintf(buf, len, "r%d", idx);
+}
+
+/* Disassemble GPR operands */
+static int __disasm_gpr(char **buf, size_t *len, const char *opnd,
+			const char *end, struct insn *insn, int idx)
+{
+	switch (opnd[1]) {
+	case 'b':
+		return psnprint_gpr8(buf, len, idx);
+	case 'w':
+		return psnprint_gpr16(buf, len, idx);
+	case 'l':
+		if (insn->opnd_bytes == 8)
+			return psnprint_gpr64(buf, len, idx);
+		else
+			return psnprint_gpr32(buf, len, idx);
+	case 'v':
+		if (insn->opnd_bytes == 8)
+			return psnprint_gpr64(buf, len, idx);
+		else if (insn->opnd_bytes == 4)
+			return psnprint_gpr32(buf, len, idx);
+		else
+			return psnprint_gpr16(buf, len, idx);
+	default:
+		return psnprintf(buf, len, "(%.*s)(bad)", end - opnd, opnd);
+	}
+}
+
+/* Disassemble GPR operand from RM bits */
+static int disasm_rm_gpr(char **buf, size_t *len, const char *opnd,
+			const char *end, struct insn *insn)
+{
+	int idx = X86_MODRM_RM(insn->modrm.bytes[0]);
+	if (insn->rex_prefix.nbytes && X86_REX_B(insn->rex_prefix.bytes[0]))
+		idx += 8;
+	return __disasm_gpr(buf, len, opnd, end, insn, idx);
+}
+
+/* Disassemble GPR operand from Reg bits */
+static int disasm_reg_gpr(char **buf, size_t *len, const char *opnd,
+			const char *end, struct insn *insn)
+{
+	int idx = X86_MODRM_REG(insn->modrm.bytes[0]);
+	if (insn->rex_prefix.nbytes) {
+		if (X86_REX_R(insn->rex_prefix.bytes[0]))
+			idx += 8;
+		else if (X86_REX_WRXB(insn->rex_prefix.bytes[0]) == 0)
+			idx += 16;
+	}
+	return __disasm_gpr(buf, len, opnd, end, insn, idx);
+}
+
+/* Disassemble GPR operand from Opcode */
+static int disasm_opcode_gpr(char **buf, size_t *len, const char *opnd,
+			     const char *end, struct insn *insn)
+{
+	int idx = X86_OPCODE_GPR(insn->opcode.bytes[insn->opcode.nbytes - 1]);
+	if (insn->rex_prefix.nbytes && X86_REX_R(insn->rex_prefix.bytes[0]))
+		idx += 8;
+	return __disasm_gpr(buf, len, opnd, end, insn, idx);
+}
+
+/* Disassemble GPR for Effective Address */
+static int __disasm_gprea(char **buf, size_t *len, const char *opnd,
+			const char *end, struct insn *insn, int idx)
+{
+	if (insn->addr_bytes == 8)
+		return psnprint_gpr64(buf, len, idx);
+	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);
+}
+
+/* Disassemble a segment prefix */
+static int __disasm_segment_prefix(char **buf, size_t *len,
+				   struct insn *insn, insn_attr_t def_attr)
+{
+	insn_attr_t attr = insn_has_segment_prefix(insn);
+
+	if (!attr) {
+		if (!def_attr)
+			return 0;
+		else
+			attr = def_attr;
+	}
+
+	attr = (attr & INAT_PFX_MASK) - INAT_SEGPFX_MIN;
+	return psnprintf(buf, len, "%s:", segreg_map[attr]);
+}
+
+static int disasm_segment_prefix(char **buf, size_t *len, struct insn *insn)
+{
+	return __disasm_segment_prefix(buf, len, insn, 0);
+}
+
+static int disasm_displacement(char **buf, size_t *len, struct insn *insn)
+{
+	__disasm_segment_prefix(buf, len, insn, INAT_PFX_DS);
+	return psnprintf(buf, len, "0x%x", insn->displacement.value);
+}
+
+/* Disassemble SIB byte */
+static int disasm_sib(char **buf, size_t *len, const char *opnd,
+			const char *end, struct insn *insn)
+{
+	int mod = X86_MODRM_MOD(insn->modrm.bytes[0]);
+	int scale = X86_SIB_SCALE(insn->sib.bytes[0]);
+	int index = X86_SIB_INDEX(insn->sib.bytes[0]);
+	int base = X86_SIB_BASE(insn->sib.bytes[0]);
+	int rexb = X86_REX_B(insn->rex_prefix.bytes[0]) * 8;
+	int rexx = X86_REX_X(insn->rex_prefix.bytes[0]) * 8;
+
+	/* Check the case which has just a displacement */
+	if (mod == 0 && index == 4 && base == 5)
+		return disasm_displacement(buf, len, insn);
+
+	disasm_segment_prefix(buf, len, insn);
+	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, "+");
+		__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);
+	}
+	return psnprintf(buf, len, "]");
+}
+
+/* Disassemble memory-register from MODR/M */
+static int disasm_modrm(char **buf, size_t *len, const char *opnd,
+			const char *end, struct insn *insn)
+{
+	int mod = X86_MODRM_MOD(insn->modrm.bytes[0]);
+	int rm = X86_MODRM_RM(insn->modrm.bytes[0]);
+
+	if (bad_modrm_operand(*opnd, mod))
+		psnprintf(buf, len, "(bad)");
+
+	if (mod == 0x3)	/* mod == 11B: GPR, MM or XMM */
+		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]",
+					  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);
+		}
+		return psnprintf(buf, len, "]");
+	}
+}
+
+static int disasm_immediate(char **buf, size_t *len, const char *opnd,
+			    const char *end, struct insn *insn)
+{
+	long long imm;
+	int size;
+
+	if (inat_has_moffset(insn->attr) && insn->addr_bytes == 8) {
+		/* 64bit memory offset */
+		unsigned long long moffs;
+		moffs = insn_field_get_uval(&insn->immediate2);
+		moffs <<= 32;
+		moffs += insn_field_get_uval(&insn->immediate);
+		__disasm_segment_prefix(buf, len, insn, INAT_PFX_DS);
+		return psnprintf(buf, len, "0x%llx", moffs);
+	}
+
+	/* Immediates are sign-extended */
+	if (inat_has_second_immediate(insn->attr) &&
+	    opnd[0] == 'I' && opnd[1] == 'b')
+		imm = insn->immediate2.value;
+	else
+		imm = insn->immediate1.value;
+
+	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);
+	}
+
+	size = insn->opnd_bytes;
+	if (opnd[1] == 'B')
+		size = 1;
+	switch (size) {
+	case 8:
+		return psnprintf(buf, len, "0x%llx", imm);
+	case 4:
+		return psnprintf(buf, len, "0x%x", (unsigned int)imm);
+	case 2:
+		return psnprintf(buf, len, "0x%x", (unsigned short)imm);
+	default:
+		return psnprintf(buf, len, "0x%x", (unsigned char)imm);
+	}
+}
+
+static int disasm_fixmem(char **buf, size_t *len, const char *opnd,
+			 const char *end, struct insn *insn)
+{
+	const char *pfx = "";
+	if (insn->addr_bytes == 4)
+		pfx = "e";
+	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',
+			 pfx, *opnd == 'x' ? 's' : 'd');
+}
+
+static int disasm_register(char **buf, size_t *len, const char *opnd,
+			   const char *end, struct insn *insn)
+{
+	char pfx[2] = {'\0', '\0'};
+
+	if (*opnd == '_') {
+		if (opnd[1] == 'r' || opnd[1] == 'e') {
+			if (insn->opnd_bytes == 4)
+				pfx[0] = 'e';
+			else if (insn->opnd_bytes == 8)
+				pfx[0] = opnd[1];
+			opnd += 2;
+			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);
+}
+
+/* Disassembe an operand */
+static int disasm_operand(char **buf, size_t *len, const char *opnd,
+			  const char *end, struct insn *insn)
+{
+	if (operand_is_register(opnd))
+		return disasm_register(buf, len, opnd, end, insn);
+	else if (operand_is_memreg(opnd))	/* Mod and RM */
+		return disasm_modrm(buf, len, opnd, end, insn);
+	else if (operand_is_imm(opnd)) /* Immedate */
+		return disasm_immediate(buf, len, opnd, end, insn);
+	else if (operand_is_gp_reg(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);
+	} else if (operand_is_dbg_reg(opnd)) {
+		int idx = X86_MODRM_REG(insn->modrm.bytes[0]);
+		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]);
+	} else if (operand_is_fixmem(opnd))
+		return disasm_fixmem(buf, len, opnd, end, insn);
+	else if (operand_is_flags(opnd))
+		/* Ignore EFLAGS/RFLAGS */
+		return 0;
+	else /* Unknown type */
+		return psnprintf(buf, len, "(%.*s)", end - opnd, opnd);
+}
+
+/**
+ * disassemble() - Disassemble given instruction
+ * @buf:	A buffer in which assembly code is stored
+ * @len:	The size of @buf
+ * @insn:	An instruction which will be disassembled
+ *
+ * This disassembles given instruction.
+ * Caller must decode @insn with insn_get_length().
+ */
+int disassemble(char *buf, size_t len, struct insn *insn)
+{
+	const char *mn_fmt;
+	const char *grp_fmt = NULL;
+	const char *prefix;
+	const char *p, *q = NULL;
+	size_t orig_len = len;
+	int ret;
+
+	/* Get the mnemonic format of given instruction */
+	mn_fmt = get_mnemonic_format(insn, &grp_fmt);
+	if (!mn_fmt)
+		return -ENOENT;
+
+	/* Put a prefix if exist */
+	prefix = get_prefix_name(insn);
+	if (prefix) {
+		ret = psnprintf(&buf, &len, "%s ", prefix);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Get operand */
+	q = p = strpbrk(mn_fmt, " |");	/* q is the end of opcode */
+	if (grp_fmt) {	/* Group opcode */
+		q = strpbrk(grp_fmt, " |");
+		mn_fmt = grp_fmt;
+		if (!p)	/* No group operand. use individual operand */
+			p = q;
+	}
+
+	/* Print opcode */
+	if (!q)
+		ret = psnprintf(&buf, &len, "%-6s ", mn_fmt);
+	else
+		ret = psnprintf(&buf, &len, "%-6.*s ", q - mn_fmt, mn_fmt);
+
+	/* Disassemble operands */
+	while (p && *p != '\0' && *p != '|' && ret >= 0) {
+		p++;
+		q = strpbrk(p, ",|");
+		if (!q)
+			q = p + strlen(p);
+		ret = disasm_operand(&buf, &len, p, q, insn);
+		if (ret < 0)
+			break;
+		if (*q == ',')
+			ret = psnprintf(&buf, &len, ",");
+		p = q;
+	}
+
+	return ret < 0 ? ret : orig_len - len;
+}
diff --git a/arch/x86/lib/mnemonic.c b/arch/x86/lib/mnemonic.c
new file mode 100644
index 0000000..0c375fb
--- /dev/null
+++ b/arch/x86/lib/mnemonic.c
@@ -0,0 +1,77 @@
+#include <linux/kernel.h>
+#include <asm/insn.h>
+#include <asm/disasm.h>
+
+/* Define mnemonic lookup table */
+#include "mnemonic-tables.c"
+
+const char *get_mnemonic_format(struct insn *insn, const char **grp)
+{
+	insn_attr_t attr;
+	const char *ret = NULL;
+	const char * const *table;
+	int n, m;
+	insn_byte_t idx, *bytes = insn->opcode.bytes;
+
+	if (!insn_complete(insn))
+		goto fail;	/* Decode it first! */
+
+	if (insn_is_avx(insn)) {
+		/* Lookup AVX instruction */
+		goto fail;
+	} else {
+		/* Lookup normal instruction */
+		idx = *bytes;
+		attr = inat_get_opcode_attribute(idx);
+		m = insn_last_prefix_id(insn);
+		/*TODO use (inat_has_variant(attr))*/
+		table = mnemonic_primary_tables[m];
+		if (!table || !table[idx])
+			table = mnemonic_primary_tables[0];
+		/* Solve escapes */
+		while (inat_is_escape(attr)) {
+			n = inat_escape_id(attr);
+			idx = *++bytes;
+			attr = inat_get_escape_attribute(idx, 0, attr);
+			if (inat_has_variant(attr))
+				table = mnemonic_escape_tables[n][m];
+			else
+				table = mnemonic_escape_tables[n][0];
+		}
+		if (table)
+			ret = table[idx];
+
+		/* Solve groups */
+		if (grp && inat_is_group(attr)) {
+			n = inat_group_id(attr);
+			idx = insn->modrm.bytes[0];
+			attr = inat_get_group_attribute(idx, 0, attr);
+			if (inat_has_variant(attr))
+				table = mnemonic_group_tables[n][m];
+			else
+				table = mnemonic_group_tables[n][0];
+			idx = X86_MODRM_REG(idx);
+			*grp = table[idx];
+		}
+	}
+	return ret;
+
+fail:
+	return NULL;
+}
+
+const char *get_prefix_name(struct insn *insn)
+{
+	int i = 0;
+	insn_attr_t attr;
+
+	for (i = 0; i < insn->prefixes.nbytes; i++) {
+		attr = inat_get_opcode_attribute(insn->prefixes.bytes[i]);
+		attr &= INAT_PFX_MASK;
+		if (attr == INAT_PFX_REPE ||
+		    attr == INAT_PFX_REPNE ||
+		    attr == INAT_PFX_LOCK)
+			return mnemonic_primary_table[insn->prefixes.bytes[i]];
+	}
+	return NULL;
+}
diff --git a/arch/x86/tools/gen-insn-mnemonic-x86.awk b/arch/x86/tools/gen-insn-mnemonic-x86.awk
new file mode 100644
index 0000000..2714f2f
--- /dev/null
+++ b/arch/x86/tools/gen-insn-mnemonic-x86.awk
@@ -0,0 +1,344 @@
+#!/bin/awk -f
+# gen-insn-mnemonic-x86.awk: X86 Instruction mnemonic table generator
+# Written by Masami Hiramatsu <masami.hiramatsu@gmail.com>
+#
+# Usage: awk -f gen-insn-mnemonic-x86.awk x86-opcode-map.txt > mnemonic-tables.c
+
+# Awk implementation sanity check
+function check_awk_implement() {
+	if (sprintf("%x", 0) != "0")
+		return "Your awk has a printf-format problem."
+	return ""
+}
+
+# Clear working vars
+function clear_vars() {
+	delete table
+	delete lptable1
+	delete lptable2
+	delete lptable3
+	eid = -1 # escape id
+	gid = -1 # group id
+	aid = -1 # AVX id
+	tname = ""
+}
+
+BEGIN {
+	# Implementation error checking
+	awkchecked = check_awk_implement()
+	if (awkchecked != "") {
+		print "Error: " awkchecked > "/dev/stderr"
+		print "Please try to use gawk." > "/dev/stderr"
+		exit 1
+	}
+
+	# Setup generating tables
+	print "/* x86 opcode map generated from x86-opcode-map.txt */"
+	print "/* Do not change this code. */\n"
+	ggid = 1
+	geid = 1
+	gaid = 0
+	delete etable
+	delete gtable
+	delete atable
+
+	opnd_expr = "^[A-Za-z/]"
+	ext_expr = "^\\("
+	sep_expr = "^\\|$"
+	group_expr = "^Grp[0-9A-Za-z]+"
+	regs_expr = "^[ABCDESR][0-9A-Z]"
+	vregs_expr = "^[re][0-9A-Z]"
+
+	lprefix1_expr = "\\(66\\)"
+	lprefix2_expr = "\\(F3\\)"
+	lprefix3_expr = "\\(F2\\)"
+	max_lprefix = 4
+
+	prefix_expr = "\\(Prefix\\)"
+	imm_expr = "^[IJAO][a-z]"
+	clear_vars()
+}
+
+function semantic_error(msg) {
+	print "Semantic error at " NR ": " msg > "/dev/stderr"
+	exit 1
+}
+
+function debug(msg) {
+	print "DEBUG: " msg
+}
+
+function array_size(arr,   i,c) {
+	c = 0
+	for (i in arr)
+		c++
+	return c
+}
+
+/^Table:/ {
+	print "/* " $0 " */"
+	if (tname != "")
+		semantic_error("Hit Table: before EndTable:.");
+}
+
+/^Referrer:/ {
+	if (NF != 1) {
+		# escape opcode table
+		ref = ""
+		for (i = 2; i <= NF; i++)
+			ref = ref $i
+		eid = escape[ref]
+		tname = sprintf("mnemonic_escape_table_%d", eid)
+	}
+}
+
+/^AVXcode:/ {
+	if (NF != 1) {
+		# AVX/escape opcode table
+		aid = $2
+		if (gaid <= aid)
+			gaid = aid + 1
+		if (tname == "")	# AVX only opcode table
+			tname = sprintf("mnemonic_avx_table_%d", $2)
+	}
+	if (aid == -1 && eid == -1)	# primary opcode table
+		tname = "mnemonic_primary_table"
+}
+
+/^GrpTable:/ {
+	print "/* " $0 " */"
+	if (!($2 in group))
+		semantic_error("No group: " $2 )
+	gid = group[$2]
+	tname = "mnemonic_group_table_" gid
+}
+
+function print_table(tbl,name,fmt,n)
+{
+	print "const char *" name " = {"
+	for (i = 0; i < n; i++) {
+		id = sprintf(fmt, i)
+		if (tbl[id])
+			print "	[" id "] = " tbl[id] ","
+	}
+	print "};"
+}
+
+/^EndTable/ {
+	if (gid != -1) {
+		# print group tables
+		if (array_size(table) != 0) {
+			print_table(table, tname "[INAT_GROUP_TABLE_SIZE]",
+				    "0x%x", 8)
+			gtable[gid,0] = tname
+		}
+		if (array_size(lptable1) != 0) {
+			print_table(lptable1, tname "_1[INAT_GROUP_TABLE_SIZE]",
+				    "0x%x", 8)
+			gtable[gid,1] = tname "_1"
+		}
+		if (array_size(lptable2) != 0) {
+			print_table(lptable2, tname "_2[INAT_GROUP_TABLE_SIZE]",
+				    "0x%x", 8)
+			gtable[gid,2] = tname "_2"
+		}
+		if (array_size(lptable3) != 0) {
+			print_table(lptable3, tname "_3[INAT_GROUP_TABLE_SIZE]",
+				    "0x%x", 8)
+			gtable[gid,3] = tname "_3"
+		}
+	} else {
+		# print primary/escaped tables
+		if (array_size(table) != 0) {
+			print_table(table, tname "[INAT_OPCODE_TABLE_SIZE]",
+				    "0x%02x", 256)
+			etable[eid,0] = tname
+			if (aid >= 0)
+				atable[aid,0] = tname
+		}
+		if (array_size(lptable1) != 0) {
+			print_table(lptable1,tname "_1[INAT_OPCODE_TABLE_SIZE]",
+				    "0x%02x", 256)
+			etable[eid,1] = tname "_1"
+			if (aid >= 0)
+				atable[aid,1] = tname "_1"
+		}
+		if (array_size(lptable2) != 0) {
+			print_table(lptable2,tname "_2[INAT_OPCODE_TABLE_SIZE]",
+				    "0x%02x", 256)
+			etable[eid,2] = tname "_2"
+			if (aid >= 0)
+				atable[aid,2] = tname "_2"
+		}
+		if (array_size(lptable3) != 0) {
+			print_table(lptable3,tname "_3[INAT_OPCODE_TABLE_SIZE]",
+				    "0x%02x", 256)
+			etable[eid,3] = tname "_3"
+			if (aid >= 0)
+				atable[aid,3] = tname "_3"
+		}
+	}
+	print ""
+	clear_vars()
+}
+
+function add_flags(old,new) {
+	if (old && new)
+		return old "\"|\"" new
+	else if (old)
+		return old
+	else
+		return new
+}
+
+function get_operand(opnd,	i,count,f8,opnds) {
+	count = split(opnd, opnds, ",")
+	# re-encode registers
+	f8 = 0
+	for (i = 1; i <= count; i++) {
+		if (match(opnds[i], "^r[A-Z][XIP]/r[189]"))
+			opnds[i] = "_vgpr"	# GPR encoded in opcode
+		else if (match(opnds[i], "^R[A-Z]*/E[A-Z]*/R[0-9]"))
+			opnds[i] = "_lgpr"	# 32 or 64 bit GPR encoded in opcode
+		else if (match(opnds[i], "^[A-Z][LH]/R[189]")) {
+			opnds[i] = "_bgpr"	# 8 bit GPR encoded in opcode
+			f8 = 1	# forcibly 8 bit cast
+		} else if (match(opnds[i], regs_expr)) {
+			if (match(opnds[i], "^[A-Z][LH]"))
+				f8 = 1
+			opnds[i] = tolower(opnds[i])
+		} else if (match(opnds[i], vregs_expr))
+			opnds[i] = "_" tolower(opnds[i])
+	}
+
+	for (i = 1; i <= count; i++) {
+		if (f8 == 1 && match(opnds[i],"Ib"))
+			opnds[i] = toupper(opnds[i])
+		if (i == 1)
+			opnd = opnds[i]
+		else
+			opnd = opnd "," opnds[i]
+	}
+	return opnd
+}
+
+/^[0-9a-f]+\:/ {
+	if (NR == 1)
+		next
+	# get index
+	idx = "0x" substr($1, 1, index($1,":") - 1)
+	if (idx in table)
+		semantic_error("Redefine " idx " in " tname)
+
+	# check if escaped opcode
+	if ("escape" == $2) {
+		if ($3 != "#")
+			semantic_error("No escaped name")
+		ref = ""
+		for (i = 4; i <= NF; i++)
+			ref = ref $i
+		if (ref in escape)
+			semantic_error("Redefine escape (" ref ")")
+		escape[ref] = geid
+		geid++
+		#table[idx] = "INAT_MAKE_ESCAPE(" escape[ref] ")"
+		table[idx] = "\"Escape_" escape[ref] "\""
+		next
+	}
+
+	variant = null
+	# converts
+	i = 2
+	while (i <= NF) {
+		opcode = $(i++)
+		ext = null
+		flags = null
+		opnd = null
+		# parse one opcode
+		if (match($i, opnd_expr))
+			opnd = get_operand($(i++))
+		if (match($i, ext_expr))
+			ext = $(i++)
+		if (match($i, sep_expr))
+			i++
+		else if (i < NF)
+			semantic_error($i " is not a separator")
+
+		# check if group opcode
+		if (match(opcode, group_expr)) {
+			if (!(opcode in group)) {
+				group[opcode] = ggid
+				ggid++
+			}
+		}
+
+		# opcode to lower characters
+		opcode = tolower(opcode)
+		if (index(opcode, "/"))
+			opcode = substr(opcode, 0, index(opcode, "/") - 1)
+		# remove near/far postfix
+		if (match(opcode, "^jmp.*"))
+			opcode = "jmp"
+		if (match(opcode, "^call.*"))
+			opcode = "call"
+		if (match(opcode, "^ret.*"))
+			opcode = "ret"
+		if (length(opnd) != 0)
+			flags = "\"" opcode " " opnd "\""
+		else
+			flags = "\"" opcode "\""
+
+		if (length(flags) == 0)
+			continue
+		# check if last prefix
+		if (match(ext, lprefix1_expr)) {
+			lptable1[idx] = add_flags(lptable1[idx], flags)
+		} else if (match(ext, lprefix2_expr)) {
+			lptable2[idx] = add_flags(lptable2[idx], flags)
+		} else if (match(ext, lprefix3_expr)) {
+			lptable3[idx] = add_flags(lptable3[idx], flags)
+		} else {
+			table[idx] = add_flags(table[idx], flags)
+		}
+	}
+}
+
+END {
+	if (awkchecked != "")
+		exit 1
+	# print primary opcode map's array
+	print "/* Primary opcode map array */"
+	print "const char * const *mnemonic_primary_tables[INAT_LSTPFX_MAX + 1] = {"
+	for (j = 0; j < max_lprefix; j++)
+		if (etable[-1,j])
+			print "	["j"] = "etable[-1,j]","
+	print "};\n"
+	# print escape opcode map's array
+	print "/* Escape opcode map array */"
+	print "const char * const *mnemonic_escape_tables[INAT_ESC_MAX + 1]" \
+	      "[INAT_LSTPFX_MAX + 1] = {"
+	for (i = 0; i < geid; i++)
+		for (j = 0; j < max_lprefix; j++)
+			if (etable[i,j])
+				print "	["i"]["j"] = "etable[i,j]","
+	print "};\n"
+	# print group opcode map's array
+	print "/* Group opcode map array */"
+	print "const char * const *mnemonic_group_tables[INAT_GRP_MAX + 1]"\
+	      "[INAT_LSTPFX_MAX + 1] = {"
+	for (i = 0; i < ggid; i++)
+		for (j = 0; j < max_lprefix; j++)
+			if (gtable[i,j])
+				print "	["i"]["j"] = "gtable[i,j]","
+	print "};\n"
+	# print AVX opcode map's array
+	print "/* AVX opcode map array */"
+	print "const char * const *mnemonic_avx_tables[X86_VEX_M_MAX + 1]"\
+	      "[INAT_LSTPFX_MAX + 1] = {"
+	for (i = 0; i < gaid; i++)
+		for (j = 0; j < max_lprefix; j++)
+			if (atable[i,j])
+				print "	["i"]["j"] = "atable[i,j]","
+	print "};"
+}
+


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 04/16] x86: Show kernel symbol in disassembler
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (2 preceding siblings ...)
  2012-04-01 16:02 ` [RFC PATCH -tip 03/16] x86: Add bogus disassembler support Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 05/16] x86: Disassemble x86-64 only instructions Masami Hiramatsu
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Show kernel symbol if the immediate value of disassembling
instruction is the destination of jump or call.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/lib/disasm.c |   25 ++++++++++++++++++++++++-
 1 files changed, 24 insertions(+), 1 deletions(-)

diff --git a/arch/x86/lib/disasm.c b/arch/x86/lib/disasm.c
index 473ae52..4abe844 100644
--- a/arch/x86/lib/disasm.c
+++ b/arch/x86/lib/disasm.c
@@ -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;


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 05/16] x86: Disassemble x86-64 only instructions
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (3 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 04/16] x86: Show kernel symbol in disassembler Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 06/16] x86: Change asm syntax to AT&T-like one Masami Hiramatsu
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Support x86-64 only instructions on disassembler.
For example, syscall and sysret.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/lib/mnemonic.c                  |   23 +++++++++++++++++++++--
 arch/x86/tools/gen-insn-mnemonic-x86.awk |   11 +++++++++--
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/arch/x86/lib/mnemonic.c b/arch/x86/lib/mnemonic.c
index 0c375fb..dfd1aab 100644
--- a/arch/x86/lib/mnemonic.c
+++ b/arch/x86/lib/mnemonic.c
@@ -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;
diff --git a/arch/x86/tools/gen-insn-mnemonic-x86.awk b/arch/x86/tools/gen-insn-mnemonic-x86.awk
index 2714f2f..14fca68 100644
--- a/arch/x86/tools/gen-insn-mnemonic-x86.awk
+++ b/arch/x86/tools/gen-insn-mnemonic-x86.awk
@@ -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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 06/16] x86: Change asm syntax to AT&T-like one
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (4 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 05/16] x86: Disassemble x86-64 only instructions Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 07/16] kdb: Provide original instruction modified by sw breakpoint Masami Hiramatsu
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

For 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.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/lib/disasm.c                    |  130 +++++++++---------------------
 arch/x86/tools/gen-insn-mnemonic-x86.awk |    5 +
 2 files changed, 41 insertions(+), 94 deletions(-)

diff --git a/arch/x86/lib/disasm.c b/arch/x86/lib/disasm.c
index 4abe844..fc5e493 100644
--- a/arch/x86/lib/disasm.c
+++ b/arch/x86/lib/disasm.c
@@ -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))
diff --git a/arch/x86/tools/gen-insn-mnemonic-x86.awk b/arch/x86/tools/gen-insn-mnemonic-x86.awk
index 14fca68..f219af0 100644
--- a/arch/x86/tools/gen-insn-mnemonic-x86.awk
+++ b/arch/x86/tools/gen-insn-mnemonic-x86.awk
@@ -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]


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 07/16] kdb: Provide original instruction modified by sw breakpoint
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (5 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 06/16] x86: Change asm syntax to AT&T-like one Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 08/16] x86/kprobes: Recover breakpoint instruction if KGDB knows Masami Hiramatsu
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Add kdb_get_saved_instr() for someone who needs to know
the original instruction in kernel text.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 include/linux/kgdb.h      |    1 +
 kernel/debug/debug_core.c |   14 ++++++++++++++
 2 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index fa39183..e1eb57c 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -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.
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index 1dc53ba..8982ba0 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -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;


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 08/16] x86/kprobes: Recover breakpoint instruction if KGDB knows
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (6 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 07/16] kdb: Provide original instruction modified by sw breakpoint Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 09/16] x86: kernel function disassembly interface Masami Hiramatsu
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Recover a breakpoint instruction which KGDB has put.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/kernel/kprobes.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index e213fc8..ddd21b8 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -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;


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 09/16] x86: kernel function disassembly interface
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (7 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 08/16] x86/kprobes: Recover breakpoint instruction if KGDB knows Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:03 ` [RFC PATCH -tip 10/16] x86/disasm: Indicate modified instructions Masami Hiramatsu
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Add 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
...

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 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(-)

diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index ae64888..5f51e05 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -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:
 #
diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h
index 5478825..9fc372b 100644
--- a/arch/x86/include/asm/kprobes.h
+++ b/arch/x86/include/asm/kprobes.h
@@ -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 */
diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c
index 90fcf62..5da917d 100644
--- a/arch/x86/kernel/kdebugfs.c
+++ b/arch/x86/kernel/kdebugfs.c
@@ -14,8 +14,12 @@
 #include <linux/stat.h>
 #include <linux/io.h>
 #include <linux/mm.h>
+#include <linux/kallsyms.h>
+#include <linux/kprobes.h>
+#include <linux/ctype.h>
 
 #include <asm/setup.h>
+#include <asm/disasm.h>
 
 struct dentry *arch_debugfs_dir;
 EXPORT_SYMBOL(arch_debugfs_dir);
@@ -202,6 +206,137 @@ err_dir:
 }
 #endif /* CONFIG_DEBUG_BOOT_PARAMS */
 
+#ifdef CONFIG_DEBUG_X86_DISASSEMBLY
+static DEFINE_MUTEX(disasm_lock);
+static char disasm_funcname[KSYM_NAME_LEN];
+static unsigned long disasm_addr;
+static unsigned long disasm_size;
+static void *disasm_pos;
+
+static ssize_t disasm_write(struct file *file, const char __user *buffer,
+			    size_t count, loff_t *ppos)
+{
+	ssize_t ret = count;
+	char *c;
+
+	if (count >= KSYM_NAME_LEN)
+		return -E2BIG;
+
+	mutex_lock(&disasm_lock);
+	if (copy_from_user(disasm_funcname, buffer, count)) {
+		ret = -EFAULT;
+		goto end;
+	}
+
+	disasm_funcname[count] = '\0';
+	c = strchr(disasm_funcname, '\n');
+	if (c)
+		*c = '\0';
+
+	disasm_addr = (unsigned long)kallsyms_lookup_name(disasm_funcname);
+	if (!disasm_addr)
+		ret = -EINVAL;
+end:
+	mutex_unlock(&disasm_lock);
+
+	return ret;
+}
+
+static void *disasm_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct insn insn;
+	char kbuf[MAX_INSN_SIZE];
+	void *p;
+
+	if (!v)
+		return NULL;
+
+	p = (void *)recover_probed_instruction(kbuf, (unsigned long)v);
+	kernel_insn_init(&insn, p);
+	insn_get_length(&insn);
+	v += insn.length;
+
+	if ((unsigned long)v >= disasm_addr + disasm_size)
+		return NULL;
+	return v;
+}
+
+static void *disasm_seq_start(struct seq_file *m, loff_t *pos)
+{
+	unsigned long offs;
+	const char *name;
+
+	mutex_lock(&disasm_lock);
+	if (!disasm_addr)
+		return NULL;
+
+	if (*pos == 0) {
+		name = kallsyms_lookup(disasm_addr, &disasm_size, &offs, NULL,
+					disasm_funcname);
+		if (!name || offs != 0)
+			return NULL;
+
+		seq_printf(m, "<%s>:\n", name);
+		return (void *)disasm_addr;
+	} else
+		return disasm_seq_next(m, disasm_pos, pos);
+}
+
+static void disasm_seq_stop(struct seq_file *m, void *v)
+{
+	disasm_pos = v;
+	mutex_unlock(&disasm_lock);
+}
+
+#define DISASM_BUF_LEN	150
+
+static int disasm_seq_show(struct seq_file *m, void *v)
+{
+	char buf[DISASM_BUF_LEN];
+	u8 kbuf[MAX_INSN_SIZE];
+	struct insn insn;
+	void *p;
+
+	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);
+
+	return 0;
+}
+
+static const struct seq_operations disasm_seq_ops = {
+	.start	= disasm_seq_start,
+	.next	= disasm_seq_next,
+	.stop	= disasm_seq_stop,
+	.show	= disasm_seq_show,
+};
+
+static int disasm_open(struct inode *inode, struct file *file)
+{
+	/* Currently we just ignore O_APPEND */
+	return seq_open(file, &disasm_seq_ops);
+}
+
+static const struct file_operations disasm_fops = {
+	.open		= disasm_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+	.write		= disasm_write,
+};
+
+static int __init disassembly_kdebugfs_init(void)
+{
+	debugfs_create_file("disassembly", S_IRUSR | S_IWUSR,
+		 arch_debugfs_dir, NULL, &disasm_fops);
+	return 0;
+}
+
+#endif /* CONFIG_DEBUG_X86_DISASSEMBLY */
+
 static int __init arch_kdebugfs_init(void)
 {
 	int error = 0;
@@ -210,6 +345,11 @@ static int __init arch_kdebugfs_init(void)
 	if (!arch_debugfs_dir)
 		return -ENOMEM;
 
+#ifdef CONFIG_DEBUG_X86_DISASSEMBLY
+	error = disassembly_kdebugfs_init();
+	if (error)
+		return error;
+#endif
 #ifdef CONFIG_DEBUG_BOOT_PARAMS
 	error = boot_params_kdebugfs_init();
 #endif


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 10/16] x86/disasm: Indicate modified instructions
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (8 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 09/16] x86: kernel function disassembly interface Masami Hiramatsu
@ 2012-04-01 16:03 ` Masami Hiramatsu
  2012-04-01 16:04 ` [RFC PATCH -tip 11/16] tracing/docs: add explanation about disassembler interface Masami Hiramatsu
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Show modified comment instead of disassembled code when
given instruction has been modified by kprobes or kdb.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/kernel/kdebugfs.c |   23 +++++++++++++++++++++--
 1 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c
index 5da917d..eccd718 100644
--- a/arch/x86/kernel/kdebugfs.c
+++ b/arch/x86/kernel/kdebugfs.c
@@ -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;
 }


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 11/16] tracing/docs: add explanation about disassembler interface
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (9 preceding siblings ...)
  2012-04-01 16:03 ` [RFC PATCH -tip 10/16] x86/disasm: Indicate modified instructions Masami Hiramatsu
@ 2012-04-01 16:04 ` Masami Hiramatsu
  2012-04-01 16:04 ` [RFC PATCH -tip 12/16] x86: Merge code dump in show_registers Masami Hiramatsu
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Add 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.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 Documentation/trace/kprobetrace.txt |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index d0d0bb9..1dc7b44 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 12/16] x86: Merge code dump in show_registers
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (10 preceding siblings ...)
  2012-04-01 16:04 ` [RFC PATCH -tip 11/16] tracing/docs: add explanation about disassembler interface Masami Hiramatsu
@ 2012-04-01 16:04 ` Masami Hiramatsu
  2012-04-01 16:04 ` [RFC PATCH -tip 13/16] x86: Disassemble support in register dump Masami Hiramatsu
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Since the code dump part of show_registers() in
dumpstack_32/64.c is a dead copy. This should be
merged.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 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(-)

diff --git a/arch/x86/include/asm/kdebug.h b/arch/x86/include/asm/kdebug.h
index d73f157..d0a0391 100644
--- a/arch/x86/include/asm/kdebug.h
+++ b/arch/x86/include/asm/kdebug.h
@@ -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);
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 1b81839..0d35e70 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -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:
diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c
index 88ec912..5c5b03f 100644
--- a/arch/x86/kernel/dumpstack_32.c
+++ b/arch/x86/kernel/dumpstack_32.c
@@ -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, &regs->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");
 }
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index 17107bd..719c6bb 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -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");
 }


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 13/16] x86: Disassemble support in register dump
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (11 preceding siblings ...)
  2012-04-01 16:04 ` [RFC PATCH -tip 12/16] x86: Merge code dump in show_registers Masami Hiramatsu
@ 2012-04-01 16:04 ` Masami Hiramatsu
  2012-04-01 16:04 ` [RFC PATCH -tip 14/16] x86: Indicate trapped address and probed address Masami Hiramatsu
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Disassemble 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.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/kernel/dumpstack.c |   81 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 81 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 0d35e70..2966142 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -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 */


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 14/16] x86: Indicate trapped address and probed address
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (12 preceding siblings ...)
  2012-04-01 16:04 ` [RFC PATCH -tip 13/16] x86: Disassemble support in register dump Masami Hiramatsu
@ 2012-04-01 16:04 ` Masami Hiramatsu
  2012-04-01 16:04 ` [RFC PATCH -tip 15/16] x86/kdb: Add x86 disassembe command Masami Hiramatsu
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Show trapped address as a comment style on disassembled
code when kernel panicks.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 arch/x86/kernel/dumpstack.c |   62 +++++++++++++++++++++++++++++++++----------
 1 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 2966142..098e61a 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -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;


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 15/16] x86/kdb: Add x86 disassembe command
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (13 preceding siblings ...)
  2012-04-01 16:04 ` [RFC PATCH -tip 14/16] x86: Indicate trapped address and probed address Masami Hiramatsu
@ 2012-04-01 16:04 ` Masami Hiramatsu
  2012-04-01 16:05 ` [RFC PATCH -tip 16/16] tools/bogodis: Add bogus disassembler tool in userspace Masami Hiramatsu
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

Add 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.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 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(-)

diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 098e61a..1441efc 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -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;
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
index db6720e..0215a67 100644
--- a/arch/x86/kernel/kgdb.c
+++ b/arch/x86/kernel/kgdb.c
@@ -39,13 +39,17 @@
 #include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/kgdb.h>
+#include <linux/kdb.h>
 #include <linux/init.h>
 #include <linux/smp.h>
 #include <linux/nmi.h>
 #include <linux/hw_breakpoint.h>
+#include <linux/kallsyms.h>
+#include <linux/kprobes.h>
 
 #include <asm/debugreg.h>
 #include <asm/apicdef.h>
+#include <asm/disasm.h>
 #include <asm/apic.h>
 #include <asm/nmi.h>
 
@@ -751,3 +755,71 @@ struct kgdb_arch arch_kgdb_ops = {
 	.remove_all_hw_break	= kgdb_remove_all_hw_break,
 	.correct_hw_break	= kgdb_correct_hw_break,
 };
+
+#if defined(CONFIG_X86_DISASSEMBLER)
+extern unsigned long find_instruction_boundary(unsigned long addr,
+						unsigned long *poffs,
+						char **modname, char *namebuf);
+
+static int kdb_disasm_printk(unsigned long addr, unsigned long *next)
+{
+	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;
+
+	kdb_printf("%p: ", v);
+	for (i = 0; i < MAX_INSN_SIZE / 2 && i < insn.length; i++)
+		kdb_printf("%02x ", ((u8 *)v)[i]);
+	if (i != MAX_INSN_SIZE / 2)
+		kdb_printf("%*s", 3 * (MAX_INSN_SIZE / 2 - i), " ");
+
+	/* print assembly code */
+	ret = disassemble(buf, DISASM_STR_LEN, &insn);
+	if (ret < 0)
+		return ret;
+	kdb_printf("%s%s\n", (fixed != addr) ? "(probed)" : "", buf);
+
+	if (i < insn.length) {
+		kdb_printf("%p: ", v + i);
+		for (; i < insn.length - 1; i++)
+			kdb_printf("%02x ", ((u8 *)v)[i]);
+		kdb_printf("%02x\n", ((u8 *)v)[i]);
+	}
+
+	if (next)
+		*next = addr + insn.length;
+
+	return 0;
+}
+
+int kdb_show_disasm(unsigned long addr, size_t len)
+{
+	unsigned long offs, eaddr = addr + len;
+	char buf[KSYM_NAME_LEN] = {0};
+	char *modname;
+
+	addr = find_instruction_boundary(addr, &offs, &modname, buf);
+	if (!addr)
+		return KDB_BADADDR;
+
+	if (modname)
+		kdb_printf("<%s+0x%lx [%s]>:\n", buf, offs, modname);
+	else
+		kdb_printf("<%s+0x%lx>:\n", buf, offs);
+
+	do {
+		kdb_disasm_printk(addr, &addr);
+	} while (addr < eaddr);
+
+	return 0;
+}
+#endif
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index 0647258..ff4b765 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -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 */
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 67b847d..f09aca4 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -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.  */


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [RFC PATCH -tip 16/16] tools/bogodis: Add bogus disassembler tool in userspace
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (14 preceding siblings ...)
  2012-04-01 16:04 ` [RFC PATCH -tip 15/16] x86/kdb: Add x86 disassembe command Masami Hiramatsu
@ 2012-04-01 16:05 ` Masami Hiramatsu
  2012-04-01 19:58 ` [RFC PATCH -tip 00/16] in-kernel x86 disassember H. Peter Anvin
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-01 16:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: Huang Ying, Ananth N Mavinakayanahalli, Frederic Weisbecker,
	H. Peter Anvin, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

This is a tiny bogus disassembler tool.
Currently, only x86 is supported.

Signed-off-by: Masami Hiramatsu <masami.hiramatsu@gmail.com>
---
 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

diff --git a/arch/x86/lib/disasm.c b/arch/x86/lib/disasm.c
index fc5e493..c6f2aef 100644
--- a/arch/x86/lib/disasm.c
+++ b/arch/x86/lib/disasm.c
@@ -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)
diff --git a/tools/bogodis/Makefile b/tools/bogodis/Makefile
new file mode 100644
index 0000000..3ef5a26d
--- /dev/null
+++ b/tools/bogodis/Makefile
@@ -0,0 +1,51 @@
+AWK := LC_ALL=C awk
+
+# Architecture identifying: copied from tools/perf/Makefile
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
+				  -e s/arm.*/arm/ -e s/sa110/arm/ \
+				  -e s/s390x/s390/ -e s/parisc64/parisc/ \
+				  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
+				  -e s/sh[234].*/sh/ )
+
+# Additional ARCH settings for x86
+ifeq ($(ARCH),i386)
+        ARCH := x86
+endif
+ifeq ($(ARCH),x86_64)
+	ARCH := x86
+endif
+
+ifneq ($(ARCH),x86)
+	$(echo "This architecture is not supported yet.")
+endif
+
+topdir := ../..
+archdir := $(topdir)/arch/$(ARCH)
+tooldir := $(archdir)/tools
+libdir := $(archdir)/lib
+incdir := $(archdir)/include/asm
+
+CFLAGS := -g -Wall -I$(topdir)/include -I$(archdir)/include
+
+SRC += $(libdir)/insn.c
+SRC += $(libdir)/inat.c
+SRC += $(libdir)/disasm.c
+SRC += $(libdir)/mnemonic.c
+SRC += $(incdir)/inat_types.h
+SRC += $(incdir)/inat.h
+SRC += $(incdir)/insn.h
+SRC += $(incdir)/disasm.h
+
+bogodis: bogodis.c inat-tables.c mnemonic-tables.c $(SRC)
+	$(CC) $(CFLAGS) -o $@ bogodis.c -I$(libdir) -I./
+
+inat-tables.c: $(libdir)/x86-opcode-map.txt $(tooldir)/gen-insn-attr-x86.awk
+	$(AWK) -f $(tooldir)/gen-insn-attr-x86.awk $< > $@
+
+mnemonic-tables.c: $(libdir)/x86-opcode-map.txt $(tooldir)/gen-insn-mnemonic-x86.awk
+	$(AWK) -f $(tooldir)/gen-insn-mnemonic-x86.awk $< > $@
+
+clean:
+	rm -f bogodis *.o inat-tables.c mnemonic-tables.c
diff --git a/tools/bogodis/bogodis.c b/tools/bogodis/bogodis.c
new file mode 100644
index 0000000..1454547
--- /dev/null
+++ b/tools/bogodis/bogodis.c
@@ -0,0 +1,202 @@
+/* bogodis - A Bogus Disassember
+ * Written by Masami Hiramatsu <masami.hiramatsu@gmail.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#define _LINUX_CTYPE_H	/* Dummy for avoiding build error */
+#define _LINUX_KERNEL_H	/* Ditto */
+#include <errno.h>
+#define BUG_ON(a)	\
+	do { fprintf(stderr, "BUG at %s:%d\n", __FILE__, __LINE__); exit(1); } while (1)
+
+/* These are for compiling instruction decoder in user space */
+#define unlikely(cond) (cond)
+
+#include <asm/insn.h>
+#include <asm/disasm.h>
+
+/* Decoder code */
+#include <inat.c>
+#include <insn.c>
+
+/* Disassembler code */
+#include <mnemonic.c>
+#include <disasm.c>
+
+static int verbose;
+static bool x86_64 = (sizeof(long) == 8);
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: bogodis [-6|-3] [-v]\n");
+	fprintf(stderr, "\t-6	64bit mode %s\n", (x86_64) ? "(default)" : "");
+	fprintf(stderr, "\t-3	32bit mode %s\n", (x86_64) ? "" : "(default)");
+	fprintf(stderr, "\t-v	Increment verbosity\n");
+	exit(1);
+}
+
+static void parse_args(int argc, char *argv[])
+{
+	int c;
+
+	while ((c = getopt(argc, argv, "63v")) != -1) {
+		switch (c) {
+		case '6':
+			x86_64 = true;
+			break;
+		case '3':
+			x86_64 = false;
+			break;
+		case 'v':
+			verbose++;
+			break;
+		default:
+			usage();
+		}
+	}
+}
+
+static void dump_field(FILE *fp, const char *name, const char *indent,
+		       struct insn_field *field)
+{
+	fprintf(fp, "%s.%s = {\n", indent, name);
+	fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n",
+		indent, field->value, field->bytes[0], field->bytes[1],
+		field->bytes[2], field->bytes[3]);
+	fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent,
+		field->got, field->nbytes);
+}
+
+static void dump_insn(FILE *fp, struct insn *insn)
+{
+	fprintf(fp, "Instruction = {\n");
+	dump_field(fp, "prefixes", "\t",	&insn->prefixes);
+	dump_field(fp, "rex_prefix", "\t",	&insn->rex_prefix);
+	dump_field(fp, "vex_prefix", "\t",	&insn->vex_prefix);
+	dump_field(fp, "opcode", "\t",		&insn->opcode);
+	dump_field(fp, "modrm", "\t",		&insn->modrm);
+	dump_field(fp, "sib", "\t",		&insn->sib);
+	dump_field(fp, "displacement", "\t",	&insn->displacement);
+	dump_field(fp, "immediate1", "\t",	&insn->immediate1);
+	dump_field(fp, "immediate2", "\t",	&insn->immediate2);
+	fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n",
+		insn->attr, insn->opnd_bytes, insn->addr_bytes);
+	fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n",
+		insn->length, insn->x86_64, insn->kaddr);
+}
+
+static int read_instruction(FILE *fp, insn_byte_t *insn_buf, size_t size)
+{
+	char *buf = NULL, *p;
+	size_t dummy;
+	int i;
+
+	memset(insn_buf, 0, size);
+
+	if (getline(&buf, &dummy, fp) < 0)
+		return -errno;
+	p = buf;
+	i = 0;
+	while (i < size) {
+		insn_buf[i++] = (insn_byte_t)strtoul(p, &p, 16);
+		if (*p == '\0' || *p == '\n' || !isspace(*p))
+			break;
+	}
+	free(buf);
+	return i;
+}
+
+/* Disassemble options */
+#define DISASM_PR_ADDR	1	/* Print address */
+#define DISASM_PR_RAW	2	/* Print raw code */
+#define DISASM_PR_ALL	(DISASM_PR_ADDR | DISASM_PR_RAW)
+
+/**
+ * snprint_assembly() - Disassemble given instruction with headers
+ * @buf:	A buffer in which assembly code is stored
+ * @len:	The size of @buf
+ * @insn:	An instruction which will be disassembled
+ * @opts:	Options
+ *
+ * This disassembles given instruction and put it into buffer with
+ * some optional information. Available option flagss are;
+ * DISASM_PR_ADDR: the address of given instruction is added.
+ * DISASM_PR_RAW:  the raw bytes of given instruction are added.
+ * Caller must initialize @insn but don't need to decode (ex insn_get_length).
+ */
+int snprint_assembly(char *buf, size_t len, struct insn *insn, int opts)
+{
+	int i = 0, ret;
+
+	insn_get_length(insn);
+	if (!insn_complete(insn))
+		return -EINVAL;
+
+	if (opts & DISASM_PR_ADDR)	/* print address */
+		psnprintf(&buf, &len, "%p: ", insn->kaddr);
+
+	if (opts & DISASM_PR_RAW) {	/* print raw instruction */
+		for (i = 0; i < MAX_INSN_SIZE / 2 && i < insn->length; i++)
+			psnprintf(&buf, &len, "%02x ", insn->kaddr[i]);
+		if (i != MAX_INSN_SIZE / 2)
+			psnprintf(&buf, &len, "%*s",
+				3 * (MAX_INSN_SIZE / 2 - i), " ");
+	}
+
+	/* print assembly code */
+	ret = disassemble(buf, len, insn);
+	if (ret < 0)
+		return ret;
+	len -= ret;
+	buf += ret;
+	psnprintf(&buf, &len, "\n");
+
+	/* print rest of raw instruction if exist */
+	if ((opts & DISASM_PR_RAW) && (i < insn->length)) {
+		if (opts & DISASM_PR_ADDR) /* print address */
+			psnprintf(&buf, &len, "%p: ", insn->kaddr + i);
+		for (; i < insn->length - 1; i++)
+			psnprintf(&buf, &len, "%02x ", insn->kaddr[i]);
+		psnprintf(&buf, &len, "%02x\n", insn->kaddr[i]);
+	}
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	insn_byte_t insn_buf[MAX_INSN_SIZE];
+	struct insn insn;
+	char buf[128];
+	const char *grp;
+	int ret;
+
+	parse_args(argc, argv);
+
+	while ((ret = read_instruction(stdin, insn_buf, MAX_INSN_SIZE)) > 0) {
+		insn_init(&insn, insn_buf, x86_64);
+		ret = snprint_assembly(buf, sizeof(buf), &insn, DISASM_PR_ALL);
+		if (ret < 0) {
+			printf("Error: reason %d\n", ret);
+			if (verbose)
+				dump_insn(stdout, &insn);
+		} else {
+			printf("%s", buf);
+			if (verbose >= 2) {
+				printf("format: %s\n",
+					get_mnemonic_format(&insn, &grp));
+				dump_insn(stdout, &insn);
+			}
+		}
+	}
+	if (verbose)
+		printf("ret = %d\n", ret);
+
+	return 0;
+}


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (15 preceding siblings ...)
  2012-04-01 16:05 ` [RFC PATCH -tip 16/16] tools/bogodis: Add bogus disassembler tool in userspace Masami Hiramatsu
@ 2012-04-01 19:58 ` H. Peter Anvin
  2012-04-02  7:04 ` Ingo Molnar
  2012-04-02 22:01 ` H. Peter Anvin
  18 siblings, 0 replies; 25+ messages in thread
From: H. Peter Anvin @ 2012-04-01 19:58 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: linux-kernel, Huang Ying, Ananth N Mavinakayanahalli,
	Frederic Weisbecker, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

On 04/01/2012 09:02 AM, Masami Hiramatsu wrote:
> 
> 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


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (16 preceding siblings ...)
  2012-04-01 19:58 ` [RFC PATCH -tip 00/16] in-kernel x86 disassember H. Peter Anvin
@ 2012-04-02  7:04 ` Ingo Molnar
  2012-04-02 22:17   ` H. Peter Anvin
  2012-04-02 22:01 ` H. Peter Anvin
  18 siblings, 1 reply; 25+ messages in thread
From: Ingo Molnar @ 2012-04-02  7:04 UTC (permalink / raw)
  To: Masami Hiramatsu, Arnaldo Carvalho de Melo, Oleg Nesterov
  Cc: linux-kernel, Huang Ying, Ananth N Mavinakayanahalli,
	Frederic Weisbecker, H. Peter Anvin, Ingo Molnar, Jason Wessel,
	Thomas Gleixner, Peter Zijlstra, Linus Torvalds


* Masami Hiramatsu <masami.hiramatsu@gmail.com> wrote:

> 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 :))

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. ]

>  - Debugfs disassembler interface for kernel function. You can disassemble
>    running kernel function on-line.

Nice :-)

>  - 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.

>  - 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

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
                   ` (17 preceding siblings ...)
  2012-04-02  7:04 ` Ingo Molnar
@ 2012-04-02 22:01 ` H. Peter Anvin
  2012-04-03  7:31   ` Ingo Molnar
  18 siblings, 1 reply; 25+ messages in thread
From: H. Peter Anvin @ 2012-04-02 22:01 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: linux-kernel, Huang Ying, Ananth N Mavinakayanahalli,
	Frederic Weisbecker, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra

On 04/01/2012 09:02 AM, Masami Hiramatsu wrote:
> 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 :))
> 

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


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-02  7:04 ` Ingo Molnar
@ 2012-04-02 22:17   ` H. Peter Anvin
  2012-04-03  7:55     ` Masami Hiramatsu
  0 siblings, 1 reply; 25+ messages in thread
From: H. Peter Anvin @ 2012-04-02 22:17 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Masami Hiramatsu, Arnaldo Carvalho de Melo, Oleg Nesterov,
	linux-kernel, Huang Ying, Ananth N Mavinakayanahalli,
	Frederic Weisbecker, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra, Linus Torvalds

On 04/02/2012 12:04 AM, Ingo Molnar wrote:
> 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


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-02 22:01 ` H. Peter Anvin
@ 2012-04-03  7:31   ` Ingo Molnar
  2012-04-03  8:39     ` Masami Hiramatsu
  2012-04-03 16:10     ` H. Peter Anvin
  0 siblings, 2 replies; 25+ messages in thread
From: Ingo Molnar @ 2012-04-03  7:31 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Masami Hiramatsu, linux-kernel, Huang Ying,
	Ananth N Mavinakayanahalli, Frederic Weisbecker, Ingo Molnar,
	Jason Wessel, Thomas Gleixner, Peter Zijlstra


* H. Peter Anvin <hpa@zytor.com> wrote:

> On 04/01/2012 09:02 AM, Masami Hiramatsu wrote:
> > 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 :))
> > 
> 
> 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.

> 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.

> [...]  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

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-02 22:17   ` H. Peter Anvin
@ 2012-04-03  7:55     ` Masami Hiramatsu
  0 siblings, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-03  7:55 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Ingo Molnar, Arnaldo Carvalho de Melo, Oleg Nesterov,
	linux-kernel, Huang Ying, Ananth N Mavinakayanahalli,
	Frederic Weisbecker, Ingo Molnar, Jason Wessel, Thomas Gleixner,
	Peter Zijlstra, Linus Torvalds

2012/4/3 H. Peter Anvin <hpa@zytor.com>:
> On 04/02/2012 12:04 AM, Ingo Molnar wrote:
>> 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,


-- 
Masami Hiramatsu
mailto:masami.hiramatsu@gmail.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-03  7:31   ` Ingo Molnar
@ 2012-04-03  8:39     ` Masami Hiramatsu
  2012-04-03 16:10     ` H. Peter Anvin
  1 sibling, 0 replies; 25+ messages in thread
From: Masami Hiramatsu @ 2012-04-03  8:39 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: H. Peter Anvin, linux-kernel, Huang Ying,
	Ananth N Mavinakayanahalli, Frederic Weisbecker, Ingo Molnar,
	Jason Wessel, Thomas Gleixner, Peter Zijlstra

2012/4/3 Ingo Molnar <mingo@kernel.org>:
>
> * H. Peter Anvin <hpa@zytor.com> wrote:
>
>> On 04/01/2012 09:02 AM, Masami Hiramatsu wrote:
>> > 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 :))
>> >
>>
>> 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.

>> 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.

OK, I'll add that.

>
>> [...]  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.

This would be good for other parameters, so I think it
should be separately done.

Thank you,

-- 
Masami Hiramatsu
mailto:masami.hiramatsu@gmail.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [RFC PATCH -tip 00/16] in-kernel x86 disassember
  2012-04-03  7:31   ` Ingo Molnar
  2012-04-03  8:39     ` Masami Hiramatsu
@ 2012-04-03 16:10     ` H. Peter Anvin
  1 sibling, 0 replies; 25+ messages in thread
From: H. Peter Anvin @ 2012-04-03 16:10 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Masami Hiramatsu, linux-kernel, Huang Ying,
	Ananth N Mavinakayanahalli, Frederic Weisbecker, Ingo Molnar,
	Jason Wessel, Thomas Gleixner, Peter Zijlstra

On 04/03/2012 12:31 AM, Ingo Molnar wrote:
> 
> 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.
> 
>> [...]  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.
> 

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

-- 
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel.  I don't speak on their behalf.


^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2012-04-03 16:10 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-01 16:02 [RFC PATCH -tip 00/16] in-kernel x86 disassember Masami Hiramatsu
2012-04-01 16:02 ` [RFC PATCH -tip 01/16] x86: Split default64 flag from force64 flag Masami Hiramatsu
2012-04-01 16:02 ` [RFC PATCH -tip 02/16] x86: Change the order of segment prefix macro Masami Hiramatsu
2012-04-01 16:02 ` [RFC PATCH -tip 03/16] x86: Add bogus disassembler support Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 04/16] x86: Show kernel symbol in disassembler Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 05/16] x86: Disassemble x86-64 only instructions Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 06/16] x86: Change asm syntax to AT&T-like one Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 07/16] kdb: Provide original instruction modified by sw breakpoint Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 08/16] x86/kprobes: Recover breakpoint instruction if KGDB knows Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 09/16] x86: kernel function disassembly interface Masami Hiramatsu
2012-04-01 16:03 ` [RFC PATCH -tip 10/16] x86/disasm: Indicate modified instructions Masami Hiramatsu
2012-04-01 16:04 ` [RFC PATCH -tip 11/16] tracing/docs: add explanation about disassembler interface Masami Hiramatsu
2012-04-01 16:04 ` [RFC PATCH -tip 12/16] x86: Merge code dump in show_registers Masami Hiramatsu
2012-04-01 16:04 ` [RFC PATCH -tip 13/16] x86: Disassemble support in register dump Masami Hiramatsu
2012-04-01 16:04 ` [RFC PATCH -tip 14/16] x86: Indicate trapped address and probed address Masami Hiramatsu
2012-04-01 16:04 ` [RFC PATCH -tip 15/16] x86/kdb: Add x86 disassembe command Masami Hiramatsu
2012-04-01 16:05 ` [RFC PATCH -tip 16/16] tools/bogodis: Add bogus disassembler tool in userspace Masami Hiramatsu
2012-04-01 19:58 ` [RFC PATCH -tip 00/16] in-kernel x86 disassember H. Peter Anvin
2012-04-02  7:04 ` Ingo Molnar
2012-04-02 22:17   ` H. Peter Anvin
2012-04-03  7:55     ` Masami Hiramatsu
2012-04-02 22:01 ` H. Peter Anvin
2012-04-03  7:31   ` Ingo Molnar
2012-04-03  8:39     ` Masami Hiramatsu
2012-04-03 16:10     ` H. Peter Anvin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.