Skip to contentSkip to conversation

in-kernel x86 disassember

RFC PATCH In review Masami Hiramatsu 3 participants 25 messages Apr 1, 2012 – Apr 3, 2012

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

cover in-kernel x86 disassember

Hi,

Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)

This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))

Currently, this can disassemble only most popular instructions in-kernel, such as non-SSE, non-MMX, non-AVX, and so on, becuase these extended instructions are rarely used in kernel :) This series supports AT&T syntax, but not fully same as objdump. Still it doesn't decode instruction suffix of operand size (w,d,q etc).

The series is also hosted on a repository on GitHub, you can get the latest version from below public repository.

git://github.com/mhiramat/linux.git

This series adds below features:

- Debugfs disassembler interface for kernel function. You can disassemble running kernel function on-line.
- Panic dump shows disassembly code instead of instruction byte stream. It generates more human-readable report. (I strongly recommend you to add a serial logger if it is enabled :))
- Disassemble command for KDB. 'dis' command is now available.
- User-land disassembly tool.

Thank you,

---

Masami Hiramatsu (16):
x86: Split default64 flag from force64 flag
x86: Change the order of segment prefix macro
x86: Add bogus disassembler support
x86: Show kernel symbol in disassembler
x86: Disassemble x86-64 only instructions
x86: Change asm syntax to AT&T-like one
kdb: Provide original instruction modified by sw breakpoint x86/kprobes: Recover breakpoint instruction if KGDB knows
x86: kernel function disassembly interface x86/disasm: Indicate modified instructions tracing/docs: add explanation about disassembler interface
x86: Merge code dump in show_registers
x86: Disassemble support in register dump
x86: Indicate trapped address and probed address x86/kdb: Add x86 disassembe command tools/bogodis: Add bogus disassembler tool in userspace

 Documentation/trace/kprobetrace.txt      |   14 +
 arch/x86/Kconfig.debug                   |   16 +
 arch/x86/include/asm/disasm.h            |   14 +
 arch/x86/include/asm/inat.h              |   38 ++
 arch/x86/include/asm/insn.h              |   16 +
 arch/x86/include/asm/kdebug.h            |    1 
 arch/x86/include/asm/kprobes.h           |    2 
 arch/x86/kernel/dumpstack.c              |  146 +++++++++
 arch/x86/kernel/dumpstack_32.c           |   26 --
 arch/x86/kernel/dumpstack_64.c           |   25 -
 arch/x86/kernel/kdebugfs.c               |  159 +++++++++
 arch/x86/kernel/kgdb.c                   |   72 ++++
 arch/x86/kernel/kprobes.c                |    9 +
 arch/x86/lib/Makefile                    |   18 +
 arch/x86/lib/disasm.c                    |  508 ++++++++++++++++++++++++++++++
 arch/x86/lib/insn.c                      |    2 
 arch/x86/lib/mnemonic.c                  |   96 ++++++
 arch/x86/tools/gen-insn-attr-x86.awk     |    7 
 arch/x86/tools/gen-insn-mnemonic-x86.awk |  352 +++++++++++++++++++++
 include/linux/kdb.h                      |    3 
 include/linux/kgdb.h                     |    1 
 kernel/debug/debug_core.c                |   14 +
 kernel/debug/kdb/kdb_main.c              |   35 ++
 tools/bogodis/Makefile                   |   51 +++
 tools/bogodis/bogodis.c                  |  202 ++++++++++++
 25 files changed, 1759 insertions(+), 68 deletions(-)
 create mode 100644 arch/x86/include/asm/disasm.h
 create mode 100644 arch/x86/lib/disasm.c
 create mode 100644 arch/x86/lib/mnemonic.c
 create mode 100644 arch/x86/tools/gen-insn-mnemonic-x86.awk
 create mode 100644 tools/bogodis/Makefile
 create mode 100644 tools/bogodis/bogodis.c

--
Masami Hiramatsu <[email protected]>

#1 lore raw

RFC PATCH 1/16 x86: Split default64 flag from force64 flag

Since current x86 instruction decoder treats default 64 bit instructions as same as force 64 bit one, eventually the decoder says the operand size is always 8 on those instructions.
However, in real, when thoes have an operand-size prefix, its operand size should be 2.
So this fixes that to split default64 from force64.

---
 arch/x86/include/asm/inat.h          |   18 ++++++++++++------
 arch/x86/include/asm/insn.h          |    2 ++
 arch/x86/lib/insn.c                  |    2 ++
 arch/x86/tools/gen-insn-attr-x86.awk |    7 +++++--
 4 files changed, 21 insertions(+), 8 deletions(-)
arch/x86/include/asm/inat.h +12 -6
index 74a2e31..55e438b 100644
@@ -83,12 +83,13 @@
 /* Flags */
 #define INAT_FLAG_OFFS	(INAT_IMM_OFFS + INAT_IMM_BITS)
 #define INAT_MODRM	(1 << (INAT_FLAG_OFFS))
-#define INAT_FORCE64	(1 << (INAT_FLAG_OFFS + 1))
-#define INAT_SCNDIMM	(1 << (INAT_FLAG_OFFS + 2))
-#define INAT_MOFFSET	(1 << (INAT_FLAG_OFFS + 3))
-#define INAT_VARIANT	(1 << (INAT_FLAG_OFFS + 4))
-#define INAT_VEXOK	(1 << (INAT_FLAG_OFFS + 5))
-#define INAT_VEXONLY	(1 << (INAT_FLAG_OFFS + 6))
+#define INAT_DEFAULT64	(1 << (INAT_FLAG_OFFS + 1))
+#define INAT_FORCE64	(1 << (INAT_FLAG_OFFS + 2))
+#define INAT_SCNDIMM	(1 << (INAT_FLAG_OFFS + 3))
+#define INAT_MOFFSET	(1 << (INAT_FLAG_OFFS + 4))
+#define INAT_VARIANT	(1 << (INAT_FLAG_OFFS + 5))
+#define INAT_VEXOK	(1 << (INAT_FLAG_OFFS + 6))
+#define INAT_VEXONLY	(1 << (INAT_FLAG_OFFS + 7))
 /* Attribute making macros for attribute tables */
 #define INAT_MAKE_PREFIX(pfx)	(pfx << INAT_PFX_OFFS)
 #define INAT_MAKE_ESCAPE(esc)	(esc << INAT_ESC_OFFS)
@@ -194,6 +195,11 @@ static inline int inat_is_force64(insn_attr_t attr)
 	return attr & INAT_FORCE64;
 }
 
+static inline int inat_is_default64(insn_attr_t attr)
+{
+	return attr & INAT_DEFAULT64;
+}
+
 static inline int inat_has_second_immediate(insn_attr_t attr)
 {
 	return attr & INAT_SCNDIMM;
arch/x86/include/asm/insn.h +2 -0
index 48eb30a..34aecec 100644
@@ -83,6 +83,8 @@ struct insn {
 #define X86_REX_X(rex) ((rex) & 2)
 #define X86_REX_B(rex) ((rex) & 1)
 
+#define X86_OPCODE_GPR(opcode) ((opcode) & 0x07)
+
 /* VEX bit flags  */
 #define X86_VEX_W(vex)	((vex) & 0x80)	/* VEX3 Byte2 */
 #define X86_VEX_R(vex)	((vex) & 0x80)	/* VEX2/3 Byte1 */
arch/x86/lib/insn.c +2 -0
index 25feb1a..e34829a 100644
@@ -257,6 +257,8 @@ void insn_get_modrm(struct insn *insn)
 
 	if (insn->x86_64 && inat_is_force64(insn->attr))
 		insn->opnd_bytes = 8;
+	if (insn->x86_64 && inat_is_default64(insn->attr) && insn->opnd_bytes == 4)
+		insn->opnd_bytes = 8;
 	modrm->got = 1;
 
 err_out:
arch/x86/tools/gen-insn-attr-x86.awk +5 -2
index 5f6a5b6..76d8889 100644
@@ -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))
#2 lore raw

RFC PATCH 2/16 x86: Change the order of segment prefix macro

In reply to Masami Hiramatsu

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

---
 arch/x86/include/asm/inat.h |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)
arch/x86/include/asm/inat.h +6 -6
index 55e438b..2a86c26 100644
@@ -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 */
#3 lore raw

RFC PATCH 3/16 x86: Add bogus disassembler support

In reply to Masami Hiramatsu

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.

---
 arch/x86/Kconfig.debug                   |    9 +
 arch/x86/include/asm/disasm.h            |   14 +
 arch/x86/include/asm/inat.h              |    8 
 arch/x86/include/asm/insn.h              |   14 +
 arch/x86/lib/Makefile                    |   18 +
 arch/x86/lib/disasm.c                    |  530 ++++++++++++++++++++++++++++++
 arch/x86/lib/mnemonic.c                  |   77 ++++
 arch/x86/tools/gen-insn-mnemonic-x86.awk |  344 +++++++++++++++++++
 8 files changed, 1009 insertions(+), 5 deletions(-)
 create mode 100644 arch/x86/include/asm/disasm.h
 create mode 100644 arch/x86/lib/disasm.c
 create mode 100644 arch/x86/lib/mnemonic.c
 create mode 100644 arch/x86/tools/gen-insn-mnemonic-x86.awk
arch/x86/Kconfig.debug +9 -0
index e46c214..ae64888 100644
@@ -172,6 +172,15 @@ config X86_DECODER_SELFTEST
 	 decoder code.
 	 If unsure, say "N".
 
+config X86_DISASSEMBLER
+	bool "Enable x86 instruction disassembler"
+	depends on INSTRUCTION_DECODER
+	---help---
+	 This option enables x86 instruction disassembler code in kernel.
+	 This will show disassembled code on console when you hit a bug or
+	 kernel panic.
+	 If unsure, say "Y" here, since this will help you to report bugs.
+
 #
 # IO delay types:
 #
arch/x86/include/asm/disasm.h new file +14 -0
new file mode 100644
index 0000000..ec5208f
@@ -0,0 +1,14 @@
+#ifndef __X86_DISASM_H__
+#define __X86_DISASM_H__
+#include <asm/insn.h>
+
+/* Mnemonic format table lookup routines */
+extern const char *get_mnemonic_format(struct insn *insn, const char **grp);
+extern const char *get_prefix_name(struct insn *insn);
+
+#define DISASM_STR_LEN	128
+
+/* Disassemble given decoded instruction */
+extern int disassemble(char *buf, size_t len, struct insn *insn);
+
+#endif	/*__X86_DISASM_H__*/
arch/x86/include/asm/inat.h +8 -0
index 2a86c26..2c90f5f 100644
@@ -50,6 +50,8 @@
 #define INAT_PFX_VEX3	14	/* 3-bytes VEX prefix */
 
 #define INAT_LSTPFX_MAX	3
+#define INAT_SEGPFX_MIN	5
+#define INAT_SEGPFX_MAX	10
 #define INAT_LGCPFX_MAX	11
 
 /* Immediate size */
@@ -116,6 +118,12 @@ static inline int inat_is_legacy_prefix(insn_attr_t attr)
 	return attr && attr <= INAT_LGCPFX_MAX;
 }
 
+static inline int inat_is_segment_prefix(insn_attr_t attr)
+{
+	attr &= INAT_PFX_MASK;
+	return INAT_SEGPFX_MIN <= attr && attr <= INAT_SEGPFX_MAX;
+}
+
 static inline int inat_is_address_size_prefix(insn_attr_t attr)
 {
 	return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ;
arch/x86/include/asm/insn.h +14 -0
index 34aecec..d96fca9 100644
@@ -82,6 +82,7 @@ struct insn {
 #define X86_REX_R(rex) ((rex) & 4)
 #define X86_REX_X(rex) ((rex) & 2)
 #define X86_REX_B(rex) ((rex) & 1)
+#define X86_REX_WRXB(rex) ((rex) & 0xf)
 
 #define X86_OPCODE_GPR(opcode) ((opcode) & 0x07)
 
@@ -168,6 +169,19 @@ static inline int insn_last_prefix_id(struct insn *insn)
 	return 0;
 }
 
+static inline insn_attr_t insn_has_segment_prefix(struct insn *insn)
+{
+	insn_attr_t attr;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		attr = inat_get_opcode_attribute(insn->prefixes.bytes[i]);
+		if (inat_is_segment_prefix(attr))
+			return attr;
+	}
+	return 0;
+}
+
 /* Offset of each field from kaddr */
 static inline int insn_offset_rex_prefix(struct insn *insn)
 {
arch/x86/lib/Makefile +13 -5
index b00f678..2571061 100644
@@ -1,18 +1,25 @@
 #
 # Makefile for x86 specific library files.
 #
+x86_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt
 
 inat_tables_script = $(srctree)/arch/x86/tools/gen-insn-attr-x86.awk
-inat_tables_maps = $(srctree)/arch/x86/lib/x86-opcode-map.txt
 quiet_cmd_inat_tables = GEN     $@
-      cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@ || rm -f $@
-
-$(obj)/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
+      cmd_inat_tables = $(AWK) -f $(inat_tables_script) $(x86_tables_maps) > $@ || rm -f $@
+$(obj)/inat-tables.c: $(inat_tables_script) $(x86_tables_maps)
 	$(call cmd,inat_tables)
 
 $(obj)/inat.o: $(obj)/inat-tables.c
 
-clean-files := inat-tables.c
+mnemonic_tables_script = $(srctree)/arch/x86/tools/gen-insn-mnemonic-x86.awk
+quiet_cmd_mnemonic_tables = GEN     $@
+      cmd_mnemonic_tables = $(AWK) -f $(mnemonic_tables_script) $(x86_tables_maps) > $@ || rm -f $@
+$(obj)/mnemonic-tables.c: $(mnemonic_tables_script) $(x86_tables_maps)
+	$(call cmd,mnemonic_tables)
+
+$(obj)/mnemonic.o: $(obj)/mnemonic-tables.c
+
+clean-files := inat-tables.c mnemonic-tables.c
 
 obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o
 
@@ -23,6 +30,7 @@ lib-y += memcpy_$(BITS).o
 lib-$(CONFIG_SMP) += rwlock.o
 lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
 lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
+lib-$(CONFIG_X86_DISASSEMBLER) += disasm.o mnemonic.o
 
 obj-y += msr.o msr-reg.o msr-reg-export.o
 
arch/x86/lib/disasm.c new file +530 -0

530 lines not on this page · raw message

arch/x86/lib/mnemonic.c new file +77 -0

77 lines not on this page · raw message

arch/x86/tools/gen-insn-mnemonic-x86.awk new file +344 -0

344 lines not on this page · raw message

#4 lore raw

RFC PATCH 4/16 x86: Show kernel symbol in disassembler

In reply to Masami Hiramatsu

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

---
 arch/x86/lib/disasm.c |   25 ++++++++++++++++++++++++-
 1 files changed, 24 insertions(+), 1 deletions(-)
arch/x86/lib/disasm.c +24 -1
index 473ae52..4abe844 100644
@@ -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;
#5 lore raw

RFC PATCH 5/16 x86: Disassemble x86-64 only instructions

In reply to Masami Hiramatsu

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

---
 arch/x86/lib/mnemonic.c                  |   23 +++++++++++++++++++++--
 arch/x86/tools/gen-insn-mnemonic-x86.awk |   11 +++++++++--
 2 files changed, 30 insertions(+), 4 deletions(-)
arch/x86/lib/mnemonic.c +21 -2
index 0c375fb..dfd1aab 100644
@@ -1,10 +1,29 @@
 #include <linux/kernel.h>
+#include <linux/string.h>
 #include <asm/insn.h>
 #include <asm/disasm.h>
 
 /* Define mnemonic lookup table */
 #include "mnemonic-tables.c"
 
+static const char *get_variant(const char *fmt, struct insn *insn)
+{
+	const char *p;
+
+	if (!fmt)
+		goto out;
+
+	if (insn->x86_64) {
+		p = strstr(fmt, "%6");
+		if (!p)
+			goto out;
+		fmt = strchr(p, ':') + 1;
+	} else if (strstr(fmt, "%6") == fmt)
+		fmt = NULL;
+out:
+	return fmt;
+}
+
 const char *get_mnemonic_format(struct insn *insn, const char **grp)
 {
 	insn_attr_t attr;
@@ -39,7 +58,7 @@ const char *get_mnemonic_format(struct insn *insn, const char **grp)
 				table = mnemonic_escape_tables[n][0];
 		}
 		if (table)
-			ret = table[idx];
+			ret = get_variant(table[idx], insn);
 
 		/* Solve groups */
 		if (grp && inat_is_group(attr)) {
@@ -51,7 +70,7 @@ const char *get_mnemonic_format(struct insn *insn, const char **grp)
 			else
 				table = mnemonic_group_tables[n][0];
 			idx = X86_MODRM_REG(idx);
-			*grp = table[idx];
+			*grp = get_variant(table[idx], insn);
 		}
 	}
 	return ret;
arch/x86/tools/gen-insn-mnemonic-x86.awk +9 -2
index 2714f2f..14fca68 100644
@@ -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
#6 lore raw

RFC PATCH 6/16 x86: Change asm syntax to AT&T-like one

In reply to Masami Hiramatsu

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.

---
 arch/x86/lib/disasm.c                    |  130 +++++++++---------------------
 arch/x86/tools/gen-insn-mnemonic-x86.awk |    5 +
 2 files changed, 41 insertions(+), 94 deletions(-)
arch/x86/lib/disasm.c +38 -92
index 4abe844..fc5e493 100644
@@ -125,35 +125,35 @@ static int bad_modrm_operand(char c, int mod)
 static int psnprint_gpr8(char **buf, size_t *len, int idx)
 {
 	if (idx < 8)
-		return psnprintf(buf, len, "%s", gpreg8_map[idx]);
+		return psnprintf(buf, len, "%%%s", gpreg8_map[idx]);
 	else if (16 < idx && idx < 24)
-		return psnprintf(buf, len, "%s", gpreg8_map2[idx - 16]);
+		return psnprintf(buf, len, "%%%s", gpreg8_map2[idx - 16]);
 	else
-		return psnprintf(buf, len, "r%dl", idx);
+		return psnprintf(buf, len, "%%r%dl", idx);
 }
 
 static int psnprint_gpr16(char **buf, size_t *len, int idx)
 {
 	if (idx < 8)
-		return psnprintf(buf, len, "%s", gpreg_map[idx]);
+		return psnprintf(buf, len, "%%%s", gpreg_map[idx]);
 	else
-		return psnprintf(buf, len, "r%dw", idx);
+		return psnprintf(buf, len, "%%r%dw", idx);
 }
 
 static int psnprint_gpr32(char **buf, size_t *len, int idx)
 {
 	if (idx < 8)
-		return psnprintf(buf, len, "e%s", gpreg_map[idx]);
+		return psnprintf(buf, len, "%%e%s", gpreg_map[idx]);
 	else
-		return psnprintf(buf, len, "r%dd", idx);
+		return psnprintf(buf, len, "%%r%dd", idx);
 }
 
 static int psnprint_gpr64(char **buf, size_t *len, int idx)
 {
 	if (idx < 8)
-		return psnprintf(buf, len, "r%s", gpreg_map[idx]);
+		return psnprintf(buf, len, "%%r%s", gpreg_map[idx]);
 	else
-		return psnprintf(buf, len, "r%d", idx);
+		return psnprintf(buf, len, "%%r%d", idx);
 }
 
 /* Disassemble GPR operands */
@@ -225,57 +225,7 @@ static int __disasm_gprea(char **buf, size_t *len, const char *opnd,
 	else if (insn->addr_bytes == 4)
 		return psnprint_gpr32(buf, len, idx);
 	else
-		return psnprintf(buf, len, "%s", gprea16_map[idx]);
-}
-
-static int get_operand_size(struct insn *insn, int type)
-{
-	int size = insn->opnd_bytes;
-
-	switch (type) {
-	case 'b':
-		size = 1;
-		break;
-	case 'w':
-		size = 2;
-		break;
-	case 'd':
-		size = 4;
-		break;
-	case 'q':
-		size = 8;
-		break;
-	case 'z':
-		if (size == 8)
-			size = 4;
-		break;
-	}
-	return size;
-}
-
-static int disasm_pointer(char **buf, size_t *len, const char *opnd,
-			  const char *end, struct insn *insn)
-{
-	const char *type = "(bad)";
-
-	if (insn->opcode.bytes[0] == X86_LEA_OPCODE)
-		return 0;
-
-	switch (get_operand_size(insn, opnd[1])) {
-	case 1:
-		type = "BYTE";
-		break;
-	case 2:
-		type = "WORD";
-		break;
-	case 4:
-		type = "DWORD";
-		break;
-	case 8:
-		type = "QWORD";
-		break;
-	}
-	return psnprintf(buf, len, "%s PTR ", type);
+		return psnprintf(buf, len, "%%%s", gprea16_map[idx]);
 }
 
 /* Disassemble a segment prefix */
@@ -292,7 +242,7 @@ static int __disasm_segment_prefix(char **buf, size_t *len,
 	}
 
 	attr = (attr & INAT_PFX_MASK) - INAT_SEGPFX_MIN;
-	return psnprintf(buf, len, "%s:", segreg_map[attr]);
+	return psnprintf(buf, len, "%%%s:", segreg_map[attr]);
 }
 
 static int disasm_segment_prefix(char **buf, size_t *len, struct insn *insn)
@@ -322,23 +272,22 @@ static int disasm_sib(char **buf, size_t *len, const char *opnd,
 		return disasm_displacement(buf, len, insn);
 
 	disasm_segment_prefix(buf, len, insn);
-	psnprintf(buf, len, "[");
+	if (mod != 0 || base == 5) {	/* With displacement offset */
+		if (insn->displacement.value < 0)
+			psnprintf(buf, len, "-0x%x", -insn->displacement.value);
+		else
+			psnprintf(buf, len, "0x%x", insn->displacement.value);
+	}
+	psnprintf(buf, len, "(");
 	if (mod != 0 || base != 5)	/* With base */
 		__disasm_gprea(buf, len, opnd, end, insn, base + rexb);
 
 	if (index != 4)	{	/* With scale * index */
-		if (mod != 0 || base != 5)
-			psnprintf(buf, len, "+");
+		psnprintf(buf, len, ",");
 		__disasm_gprea(buf, len, opnd, end, insn, index + rexx);
-		psnprintf(buf, len, "*%x", 1 << scale);
-	}
-	if (mod != 0 || base == 5) {	/* With displacement offset */
-		if (insn->displacement.value < 0)
-			psnprintf(buf, len, "-0x%x", -insn->displacement.value);
-		else
-			psnprintf(buf, len, "+0x%x", insn->displacement.value);
+		psnprintf(buf, len, ",%x", 1 << scale);
 	}
-	return psnprintf(buf, len, "]");
+	return psnprintf(buf, len, ")");
 }
 
 /* Disassemble memory-register from MODR/M */
@@ -355,30 +304,28 @@ static int disasm_modrm(char **buf, size_t *len, const char *opnd,
 		return disasm_rm_gpr(buf, len, opnd, end, insn);
 
 	/* Memory addressing */
-	disasm_pointer(buf, len, opnd, end, insn);
-
 	if (insn->sib.nbytes)	/* SIB addressing */
 		return disasm_sib(buf, len, opnd, end, insn);
 
 	if (mod == 0 && rm == 5) {	/* displacement only */
 		if (insn_rip_relative(insn))	/* RIP relative */
-			return psnprintf(buf, len, "[rip+0x%x]",
+			return psnprintf(buf, len, "0x%x(%rip)",
 					  insn->displacement.value);
 		else
 			return disasm_displacement(buf, len, insn);
 	} else {
 		disasm_segment_prefix(buf, len, insn);
-		psnprintf(buf, len, "[");
-		if (insn->rex_prefix.nbytes && X86_REX_B(insn->rex_prefix.bytes[0]))
-			rm += 8;
-		__disasm_gprea(buf, len, opnd, end, insn, rm);
 		if (mod != 0) {
 			if (insn->displacement.value < 0)
 				psnprintf(buf, len, "-0x%x", -insn->displacement.value);
 			else
-				psnprintf(buf, len, "+0x%x", insn->displacement.value);
+				psnprintf(buf, len, "0x%x", insn->displacement.value);
 		}
-		return psnprintf(buf, len, "]");
+		psnprintf(buf, len, "(");
+		if (insn->rex_prefix.nbytes && X86_REX_B(insn->rex_prefix.bytes[0]))
+			rm += 8;
+		__disasm_gprea(buf, len, opnd, end, insn, rm);
+		return psnprintf(buf, len, ")");
 	}
 }
 
@@ -416,13 +363,13 @@ static int disasm_immediate(char **buf, size_t *len, const char *opnd,
 		size = 1;
 	switch (size) {
 	case 8:
-		return psnprintf(buf, len, "0x%llx", imm);
+		return psnprintf(buf, len, "$0x%llx", imm);
 	case 4:
-		return psnprintf(buf, len, "0x%x", (unsigned int)imm);
+		return psnprintf(buf, len, "$0x%x", (unsigned int)imm);
 	case 2:
-		return psnprintf(buf, len, "0x%x", (unsigned short)imm);
+		return psnprintf(buf, len, "$0x%x", (unsigned short)imm);
 	default:
-		return psnprintf(buf, len, "0x%x", (unsigned char)imm);
+		return psnprintf(buf, len, "$0x%x", (unsigned char)imm);
 	}
 }
 
@@ -435,8 +382,7 @@ static int disasm_fixmem(char **buf, size_t *len, const char *opnd,
 	else if (insn->addr_bytes == 8)
 		pfx = "r";
 
-	disasm_pointer(buf, len, opnd, end, insn);
-	return psnprintf(buf, len, "%cs:[%s%ci]", *opnd == 'x' ? 'd' : 'e',
+	return psnprintf(buf, len, "%%%cs:(%%%s%ci)", *opnd == 'x' ? 'd' : 'e',
 			 pfx, *opnd == 'x' ? 's' : 'd');
 }
 
@@ -452,11 +398,11 @@ static int disasm_register(char **buf, size_t *len, const char *opnd,
 			else if (insn->opnd_bytes == 8)
 				pfx[0] = opnd[1];
 			opnd += 2;
-			return psnprintf(buf, len, "%s%.*s", pfx, end - opnd, opnd);
+			return psnprintf(buf, len, "%%%s%.*s", pfx, end - opnd, opnd);
 		} else
 			return disasm_opcode_gpr(buf, len, opnd, end, insn);
 	} else
-		return psnprintf(buf, len, "%.*s", end - opnd, opnd);
+		return psnprintf(buf, len, "%%%.*s", end - opnd, opnd);
 }
 
 /* Disassembe an operand */
@@ -473,13 +419,13 @@ static int disasm_operand(char **buf, size_t *len, const char *opnd,
 		return disasm_reg_gpr(buf, len, opnd, end, insn);
 	else if (operand_is_ctl_reg(opnd)) {
 		int idx = X86_MODRM_REG(insn->modrm.bytes[0]);
-		return psnprintf(buf, len, "cr%d", idx);
+		return psnprintf(buf, len, "%%cr%d", idx);
 	} else if (operand_is_dbg_reg(opnd)) {
 		int idx = X86_MODRM_REG(insn->modrm.bytes[0]);
-		return psnprintf(buf, len, "dr%d", idx);
+		return psnprintf(buf, len, "%%dr%d", idx);
 	} else if (operand_is_seg_reg(opnd)) {
 		int idx = X86_MODRM_REG(insn->modrm.bytes[0]);
-		return psnprintf(buf, len, "%s", segreg_map[idx]);
+		return psnprintf(buf, len, "%%%s", segreg_map[idx]);
 	} else if (operand_is_fixmem(opnd))
 		return disasm_fixmem(buf, len, opnd, end, insn);
 	else if (operand_is_flags(opnd))
arch/x86/tools/gen-insn-mnemonic-x86.awk +3 -2
index 14fca68..f219af0 100644
@@ -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]
#7 lore raw

RFC PATCH 7/16 kdb: Provide original instruction modified by sw breakpoint

In reply to Masami Hiramatsu

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

---
 include/linux/kgdb.h      |    1 +
 kernel/debug/debug_core.c |   14 ++++++++++++++
 2 files changed, 15 insertions(+), 0 deletions(-)
include/linux/kgdb.h +1 -0
index fa39183..e1eb57c 100644
@@ -209,6 +209,7 @@ extern void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc);
 extern int kgdb_validate_break_address(unsigned long addr);
 extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
 extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
+extern int kgdb_get_saved_instr(unsigned long addr, unsigned char *buf);
 
 /**
  *	kgdb_arch_late - Perform any architecture specific initalization.
kernel/debug/debug_core.c +14 -0
index 1dc53ba..8982ba0 100644
@@ -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;
#8 lore raw

RFC PATCH 8/16 x86/kprobes: Recover breakpoint instruction if KGDB knows

In reply to Masami Hiramatsu

Recover a breakpoint instruction which KGDB has put.

---
 arch/x86/kernel/kprobes.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)
arch/x86/kernel/kprobes.c +9 -0
index e213fc8..ddd21b8 100644
@@ -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;
#9 lore raw

RFC PATCH 9/16 x86: kernel function disassembly interface

In reply to Masami Hiramatsu

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
...
---
 arch/x86/Kconfig.debug         |    7 ++
 arch/x86/include/asm/kprobes.h |    2 +
 arch/x86/kernel/kdebugfs.c     |  140 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 149 insertions(+), 0 deletions(-)
arch/x86/Kconfig.debug +7 -0
index ae64888..5f51e05 100644
@@ -181,6 +181,13 @@ config X86_DISASSEMBLER
 	 kernel panic.
 	 If unsure, say "Y" here, since this will help you to report bugs.
 
+config DEBUG_X86_DISASSEMBLY
+	bool "x86 instruction disassembly interface on debugfs"
+	depends on X86_DISASSEMBLER && DEBUG_FS
+	---help---
+	 This option adds /sys/kernel/debug/x86/disassembly interface
+	 for disassembling a kernel function.
+
 #
 # IO delay types:
 #
arch/x86/include/asm/kprobes.h +2 -0
index 5478825..9fc372b 100644
@@ -114,4 +114,6 @@ struct kprobe_ctlblk {
 extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
 				    unsigned long val, void *data);
+extern unsigned long recover_probed_instruction(kprobe_opcode_t *buf,
+						unsigned long addr);
 #endif /* _ASM_X86_KPROBES_H */
arch/x86/kernel/kdebugfs.c +140 -0

160 lines not on this page · raw message

#10 lore raw

RFC PATCH 10/16 x86/disasm: Indicate modified instructions

In reply to Masami Hiramatsu

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

---
 arch/x86/kernel/kdebugfs.c |   23 +++++++++++++++++++++--
 1 files changed, 21 insertions(+), 2 deletions(-)
arch/x86/kernel/kdebugfs.c +21 -2
index 5da917d..eccd718 100644
@@ -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;
 }
#11 lore raw

RFC PATCH 11/16 tracing/docs: add explanation about disassembler interface

In reply to Masami Hiramatsu

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.

---
 Documentation/trace/kprobetrace.txt |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)
Documentation/trace/kprobetrace.txt +14 -0
index d0d0bb9..1dc7b44 100644
@@ -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
#12 lore raw

RFC PATCH 12/16 x86: Merge code dump in show_registers

In reply to Masami Hiramatsu

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

---
 arch/x86/include/asm/kdebug.h  |    1 +
 arch/x86/kernel/dumpstack.c    |   31 +++++++++++++++++++++++++++++++
 arch/x86/kernel/dumpstack_32.c |   26 +-------------------------
 arch/x86/kernel/dumpstack_64.c |   25 +------------------------
 4 files changed, 34 insertions(+), 49 deletions(-)
arch/x86/include/asm/kdebug.h +1 -0
index d73f157..d0a0391 100644
@@ -24,6 +24,7 @@ enum die_val {
 extern void printk_address(unsigned long address, int reliable);
 extern void die(const char *, struct pt_regs *,long);
 extern int __must_check __die(const char *, struct pt_regs *, long);
+extern void show_code_dump(struct pt_regs *regs);
 extern void show_registers(struct pt_regs *regs);
 extern void show_trace(struct task_struct *t, struct pt_regs *regs,
 		       unsigned long *sp, unsigned long bp);
arch/x86/kernel/dumpstack.c +31 -0
index 1b81839..0d35e70 100644
@@ -292,6 +292,37 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
 	return 0;
 }
 
+void __kprobes show_code_dump(struct pt_regs *regs)
+{
+	int i;
+	unsigned int code_prologue = code_bytes * 43 / 64;
+	unsigned int code_len = code_bytes;
+	unsigned char c;
+	u8 *ip;
+
+	ip = (u8 *)regs->ip - code_prologue;
+	if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
+		/* try starting at IP */
+		ip = (u8 *)regs->ip;
+		code_len = code_len - code_prologue + 1;
+	}
+	for (i = 0; i < code_len; i++, ip++) {
+		if (ip < (u8 *)PAGE_OFFSET ||
+				probe_kernel_address(ip, c)) {
+#ifdef CONFIG_X86_32
+			printk(KERN_CONT " Bad EIP value.");
+#else
+			printk(KERN_CONT " Bad RIP value.");
+#endif
+			break;
+		}
+		if (ip == (u8 *)regs->ip)
+			printk(KERN_CONT "<%02x> ", c);
+		else
+			printk(KERN_CONT "%02x ", c);
+	}
+}
+
 /*
  * This is gone through when something in the kernel has done something bad
  * and is about to be terminated:
arch/x86/kernel/dumpstack_32.c +1 -25
index 88ec912..5c5b03f 100644
@@ -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");
 }
arch/x86/kernel/dumpstack_64.c +1 -24
index 17107bd..719c6bb 100644
@@ -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");
 }
#13 lore raw

RFC PATCH 13/16 x86: Disassemble support in register dump

In reply to Masami Hiramatsu

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.

---
 arch/x86/kernel/dumpstack.c |   81 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 81 insertions(+), 0 deletions(-)
arch/x86/kernel/dumpstack.c +81 -0
index 0d35e70..2966142 100644
@@ -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 */
#14 lore raw

RFC PATCH 14/16 x86: Indicate trapped address and probed address

In reply to Masami Hiramatsu

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

---
 arch/x86/kernel/dumpstack.c |   62 +++++++++++++++++++++++++++++++++----------
 1 files changed, 48 insertions(+), 14 deletions(-)
arch/x86/kernel/dumpstack.c +48 -14
index 2966142..098e61a 100644
@@ -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;
#15 lore raw

RFC PATCH 15/16 x86/kdb: Add x86 disassembe command

In reply to Masami Hiramatsu

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.

---
 arch/x86/kernel/dumpstack.c |    6 ++--
 arch/x86/kernel/kgdb.c      |   72 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/kdb.h         |    3 ++
 kernel/debug/kdb/kdb_main.c |   35 +++++++++++++++++++++
 4 files changed, 113 insertions(+), 3 deletions(-)
arch/x86/kernel/dumpstack.c +3 -3
index 098e61a..1441efc 100644
@@ -297,9 +297,9 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
 #ifdef CONFIG_X86_DISASSEMBLER
 
 /* Find the instruction boundary address */
-static unsigned long find_instruction_boundary(unsigned long saddr,
-						unsigned long *poffs,
-						char **modname, char *namebuf)
+unsigned long find_instruction_boundary(unsigned long saddr,
+					unsigned long *poffs,
+					char **modname, char *namebuf)
 {
 	kprobe_opcode_t buf[MAX_INSN_SIZE];
 	unsigned long offs, addr, fixed;
arch/x86/kernel/kgdb.c +72 -0

88 lines not on this page · raw message

include/linux/kdb.h +3 -0
index 0647258..ff4b765 100644
@@ -166,4 +166,7 @@ enum {
 extern int kdbgetintenv(const char *, int *);
 extern int kdb_set(int, const char **);
 
+/* Some architectures support disassembling in kernel */
+extern int kdb_show_disasm(unsigned long addr, size_t len);
+
 #endif	/* !_KDB_H */
kernel/debug/kdb/kdb_main.c +35 -0
index 67b847d..f09aca4 100644
@@ -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.  */
#16 lore raw

RFC PATCH 16/16 tools/bogodis: Add bogus disassembler tool in userspace

In reply to Masami Hiramatsu

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

---
 arch/x86/lib/disasm.c   |    9 ++
 tools/bogodis/Makefile  |   51 ++++++++++++
 tools/bogodis/bogodis.c |  202 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 262 insertions(+), 0 deletions(-)
 create mode 100644 tools/bogodis/Makefile
 create mode 100644 tools/bogodis/bogodis.c
arch/x86/lib/disasm.c +9 -0
index fc5e493..c6f2aef 100644
@@ -7,7 +7,9 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/errno.h>
+#ifdef __KERNEL__
 #include <linux/kallsyms.h>
+#endif
 
 #include <asm/disasm.h>
 
@@ -30,6 +32,7 @@ static int psnprintf(char **buf, size_t *len, const char *fmt, ...)
 	return ret;
 }
 
+#ifdef __KERNEL__
 /* Print address with symbol */
 static int psnprint_symbol(char **buf, size_t *len, unsigned long addr)
 {
@@ -50,6 +53,12 @@ static int psnprint_symbol(char **buf, size_t *len, unsigned long addr)
 
 	return psnprintf(buf, len, ">");
 }
+#else
+static int psnprint_symbol(char **buf, size_t *len, unsigned long addr)
+{
+	return psnprintf(buf, len, "%lx", addr);
+}
+#endif
 
 /* Operand classifiers */
 static bool operand_is_register(const char *p)
tools/bogodis/Makefile new file +51 -0

51 lines not on this page · raw message

tools/bogodis/bogodis.c new file +202 -0

202 lines not on this page · raw message

#17 lore raw
In reply to Masami Hiramatsu
Quoted 6 lines · Masami Hiramatsu

This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))

Very day-appropriate ;)

-hpa

#18 lore raw
In reply to Masami Hiramatsu
Quoted 8 lines · Masami Hiramatsu

Hi,

Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)

Quoted 4 lines

This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))

Nice :-)

Wrt. testing: just wondering, could we eventually attempt to create a user-space testcase for this as well? I.e. if we tried to have a switch to emulate objdump output, we could check that the in-kernel disassembler outputs the same sequence as objdump -d, or so.

[ I realize that this does not cover SSE instructions, which do
sometimes occur in the vmlinux - but 99% of the instruction stream is regular and would be a nice testcase. ]

Quoted 2 lines

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

Nice :-)

Quoted 3 lines

- Panic dump shows disassembly code instead of instruction byte stream. It generates more human-readable report. (I strongly recommend you to add a serial logger if it is enabled :))

This is the most useful short-term practical aspect I suspect.

Quoted 2 lines

- Disassemble command for KDB. 'dis' command is now available.
- User-land disassembly tool.

It would be nice to extend the output beyond the boring GNU tooling, for example to auto-label branch targets instead of relying on debuginfo.

This could be used for better visualization as well, instead of the boring and hard to read GNU output:

ffffffff8175d500 <_raw_spin_lock>:

ffffffff8175d500:	55                   	push   %rbp
ffffffff8175d501:	b8 00 00 01 00       	mov    $0x10000,%eax
ffffffff8175d506:	48 89 e5             	mov    %rsp,%rbp
ffffffff8175d509:	f0 0f c1 07          	lock xadd %eax,(%rdi)
ffffffff8175d50d:	89 c2                	mov    %eax,%edx
ffffffff8175d50f:	c1 ea 10             	shr    $0x10,%edx
ffffffff8175d512:	66 39 c2             	cmp    %ax,%dx
ffffffff8175d515:	74 13                	je     ffffffff8175d52a <_raw_spin_lock+0x2a>
ffffffff8175d517:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
ffffffff8175d51e:	00 00 
ffffffff8175d520:	f3 90                	pause  
ffffffff8175d522:	0f b7 07             	movzwl (%rdi),%eax
ffffffff8175d525:	66 39 d0             	cmp    %dx,%ax
ffffffff8175d528:	75 f6                	jne    ffffffff8175d520 <_raw_spin_lock+0x20>
ffffffff8175d52a:	5d                   	pop    %rbp
ffffffff8175d52b:	c3                   	retq   
ffffffff8175d52c:	0f 1f 40 00          	nopl   0x0(%rax)

the default 'human readable' output could be something much more intelligent, like:

<_raw_spin_lock>:

             push       %rbp
             mov        $0x10000, %eax
             mov        %rsp, %rbp
             lock xadd  %eax, (%rdi)
             mov        %eax, %edx
             shr        $0x10, %edx
             cmp        %ax, %dx
             je         L2       #-----------------------------.
             nop-7                                             |
                                                               |
L1:          pause                          <-------------.    |
             movzwl     (%rdi), %eax                      |    |
             cmp        %dx, %ax                          |    |
             jne        L1       #------------------------'    |
                                                               |

L2: pop %rbp <------------------'
retq

This is much more readable, right? Yet it carries all the essential information that the original output one carried.

If vector instructions (SEE, MMX, AVX) are in your list to support then it would be and interesting use to combine this with perf on x86 - which uses objdump right now. Perf could use a programmatic, librarized disassembler for its assembly annotation code.

That would allow new UI features like:

- proper highlighting of jump/branch instructions and navigation along branch instructions (and visualization of possible execution flow) as well.

- register modification and lifetime highlighting. If I click on 'rax' then the output could show how this register gets touched by the code, explicitly and implicitly (a common assembly coding pitfall)

- summarization of usually irrelevant details, like the nop-7 example above.

Another very interesting usecase would be to invert it and create a simpler parser and an in-kernel *assembler*: a GAS replacement in essence. We could build the kernel using its own assembler.

That could also be used for safe sandboxing: the disassembler could be combined with the assembler to ensure that binary code submitted to the kernel is 'safe' to execute - even in kernel-space. A sha1 hash could be used to cache already checked, 'safe' modules of code.

Thanks,

Ingo

#19 lore raw
Quoted 3 lines · Ingo Molnar

Perf could use
a programmatic, librarized disassembler for its assembly annotation code.

BTW, it should be pretty trivial to librarize the disassembler from NASM. Even more so, I think this might have already been done with Yasm.

-hpa

#20 lore raw

2012/4/3 H. Peter Anvin <[email protected]>:

Quoted 7 lines · Ingo Molnar
Quoted 3 lines

Perf could use
a programmatic, librarized disassembler for its assembly annotation code.

BTW, it should be pretty trivial to librarize the disassembler from NASM.  Even more so, I think this might have already been done with Yasm.

Technically, yes. There are already many disassemblers. This series was just started for my interesting of how disassember works and helps kprobes debugging :)

Oh, and also, I'd like to try reusing x86-opcode-map.txt which is already in the kernel tree. :)

Thank you,

Signature · 2 lines
Masami Hiramatsu
mailto:[email protected]
#21 lore raw
In reply to Masami Hiramatsu
Quoted 8 lines · Masami Hiramatsu

Hi,

Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)

Quoted 5 lines

This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))

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

#22 lore raw
Quoted 15 lines · H. Peter Anvin
Quoted 8 lines · Masami Hiramatsu

Hi,

Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)

Quoted 5 lines

This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))

Quoted 4 lines

This is cool, but I have one major reservation about it: it will make kernel panics take a lot more screen real estate without containing more information, and we already have problems with things scrolling off way too easily.

Yes, I'm not sure we want to do it by default.

Quoted 3 lines

For that reason I would like to request that this *only* enabled by an explicit command-line option or similar (disasm_oops, maybe?), so that the user has to opt-in.

The existing oops setup knob is an early_param() in kernel/panic.c, "oops=".

I'd suggesting extending that in an obvious way. Currently the only option that exists is "oops=panic", so a comma delimited list of attributes would be the natural extension, allowing:

oops=panic
oops=panic,disasm
oops=disasm

Detail: it should do a strncmp(5, str, "disas"), so that every usual variant works: oops=disasm, oops=disassemble, etc.

Quoted 2 lines

[...] In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.

Probably a DEBUG .config option as well, so that distros can enable it. OTOH, CONFIG_CMDLINE allows the setting of such parameters as well.

Thanks,

Ingo

#23 lore raw

2012/4/3 Ingo Molnar <[email protected]>:

Quoted 18 lines · H. Peter Anvin
Quoted 16 lines · Masami Hiramatsu
Quoted 13 lines

Hi,

Here is a series of patches of the in-kernel x86 disassembler for the latest tip tree.
This will show you a pretty disassembled code instead of just a digital code sequence when you gets a kernel panic etc. (I know, we also have script/decodecode for the panic use)

This feature is not for users, but mainly for kernel developers who can understand disassembly code of x86 ;). This is just like a joke feature in kernel. (yeah, I spend my spare time for this. It's my fun :))

Quoted 6 lines
Quoted 4 lines

This is cool, but I have one major reservation about it: it will make kernel panics take a lot more screen real estate without containing more information, and we already have problems with things scrolling off way too easily.

Yes, I'm not sure we want to do it by default.

Agreed.

Quoted 11 lines
Quoted 3 lines

For that reason I would like to request that this *only* enabled by an explicit command-line option or similar (disasm_oops, maybe?), so that the user has to opt-in.

The existing oops setup knob is an early_param() 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:

Quoted 6 lines

       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.

Quoted 7 lines
Quoted 3 lines

[...]  In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.

Probably a DEBUG .config option as well, so that distros can enable it. OTOH, CONFIG_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,

Signature · 2 lines
Masami Hiramatsu
mailto:[email protected]
#24 lore raw
In reply to Ingo Molnar
Quoted 15 lines · Ingo Molnar

The existing oops setup knob is an early_param() in kernel/panic.c, "oops=".

I'd suggesting extending that in an obvious way. Currently the only option that exists is "oops=panic", so a comma delimited list of attributes would be the natural extension, allowing:

oops=panic
oops=panic,disasm
oops=disasm

Detail: it should do a strncmp(5, str, "disas"), so that every usual variant works: oops=disasm, oops=disassemble, etc.

Quoted 7 lines
Quoted 2 lines

[...] In other words, if *you* are debugging your own kernel, and don't expect to ship oopses off to someone else.

Probably a DEBUG .config option as well, so that distros can enable it. OTOH, CONFIG_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

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