Skip to content

Commit 8316385

Browse files
committed
Merge branch 'x86-debug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 debug updates from Ingo Molnar: "This contains the x86 oops code printing reorganization and cleanups from Borislav Betkov, with a particular focus in enhancing opcode dumping all around" * 'x86-debug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/dumpstack: Explain the reasoning for the prologue and buffer size x86/dumpstack: Save first regs set for the executive summary x86/dumpstack: Add a show_ip() function x86/fault: Dump user opcode bytes on fatal faults x86/dumpstack: Add loglevel argument to show_opcodes() x86/dumpstack: Improve opcodes dumping in the code section x86/dumpstack: Carve out code-dumping into a function x86/dumpstack: Unexport oops_begin() x86/dumpstack: Remove code_bytes
2 parents 0afe832 + 4dba072 commit 8316385

File tree

5 files changed

+80
-86
lines changed

5 files changed

+80
-86
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,6 @@
587587
Sets the size of memory pool for coherent, atomic dma
588588
allocations, by default set to 256K.
589589

590-
code_bytes [X86] How many bytes of object code to print
591-
in an oops report.
592-
Range: 0 - 8192
593-
Default: 64
594-
595590
com20020= [HW,NET] ARCnet - COM20020 chipset
596591
Format:
597592
<io>[,<irq>[,<nodeID>[,<backplane>[,<ckp>[,<timeout>]]]]]

arch/x86/include/asm/stacktrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,6 @@ static inline unsigned long caller_frame_pointer(void)
111111
return (unsigned long)frame;
112112
}
113113

114+
void show_opcodes(u8 *rip, const char *loglvl);
115+
void show_ip(struct pt_regs *regs, const char *loglvl);
114116
#endif /* _ASM_X86_STACKTRACE_H */

arch/x86/kernel/dumpstack.c

Lines changed: 70 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
#include <asm/stacktrace.h>
2323
#include <asm/unwind.h>
2424

25+
#define OPCODE_BUFSIZE 64
26+
2527
int panic_on_unrecovered_nmi;
2628
int panic_on_io_nmi;
27-
static unsigned int code_bytes = 64;
2829
static int die_counter;
2930

31+
static struct pt_regs exec_summary_regs;
32+
3033
bool in_task_stack(unsigned long *stack, struct task_struct *task,
3134
struct stack_info *info)
3235
{
@@ -69,9 +72,62 @@ static void printk_stack_address(unsigned long address, int reliable,
6972
printk("%s %s%pB\n", log_lvl, reliable ? "" : "? ", (void *)address);
7073
}
7174

75+
/*
76+
* There are a couple of reasons for the 2/3rd prologue, courtesy of Linus:
77+
*
78+
* In case where we don't have the exact kernel image (which, if we did, we can
79+
* simply disassemble and navigate to the RIP), the purpose of the bigger
80+
* prologue is to have more context and to be able to correlate the code from
81+
* the different toolchains better.
82+
*
83+
* In addition, it helps in recreating the register allocation of the failing
84+
* kernel and thus make sense of the register dump.
85+
*
86+
* What is more, the additional complication of a variable length insn arch like
87+
* x86 warrants having longer byte sequence before rIP so that the disassembler
88+
* can "sync" up properly and find instruction boundaries when decoding the
89+
* opcode bytes.
90+
*
91+
* Thus, the 2/3rds prologue and 64 byte OPCODE_BUFSIZE is just a random
92+
* guesstimate in attempt to achieve all of the above.
93+
*/
94+
void show_opcodes(u8 *rip, const char *loglvl)
95+
{
96+
unsigned int code_prologue = OPCODE_BUFSIZE * 2 / 3;
97+
u8 opcodes[OPCODE_BUFSIZE];
98+
u8 *ip;
99+
int i;
100+
101+
printk("%sCode: ", loglvl);
102+
103+
ip = (u8 *)rip - code_prologue;
104+
if (probe_kernel_read(opcodes, ip, OPCODE_BUFSIZE)) {
105+
pr_cont("Bad RIP value.\n");
106+
return;
107+
}
108+
109+
for (i = 0; i < OPCODE_BUFSIZE; i++, ip++) {
110+
if (ip == rip)
111+
pr_cont("<%02x> ", opcodes[i]);
112+
else
113+
pr_cont("%02x ", opcodes[i]);
114+
}
115+
pr_cont("\n");
116+
}
117+
118+
void show_ip(struct pt_regs *regs, const char *loglvl)
119+
{
120+
#ifdef CONFIG_X86_32
121+
printk("%sEIP: %pS\n", loglvl, (void *)regs->ip);
122+
#else
123+
printk("%sRIP: %04x:%pS\n", loglvl, (int)regs->cs, (void *)regs->ip);
124+
#endif
125+
show_opcodes((u8 *)regs->ip, loglvl);
126+
}
127+
72128
void show_iret_regs(struct pt_regs *regs)
73129
{
74-
printk(KERN_DEFAULT "RIP: %04x:%pS\n", (int)regs->cs, (void *)regs->ip);
130+
show_ip(regs, KERN_DEFAULT);
75131
printk(KERN_DEFAULT "RSP: %04x:%016lx EFLAGS: %08lx", (int)regs->ss,
76132
regs->sp, regs->flags);
77133
}
@@ -267,7 +323,6 @@ unsigned long oops_begin(void)
267323
bust_spinlocks(1);
268324
return flags;
269325
}
270-
EXPORT_SYMBOL_GPL(oops_begin);
271326
NOKPROBE_SYMBOL(oops_begin);
272327

273328
void __noreturn rewind_stack_do_exit(int signr);
@@ -287,6 +342,9 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
287342
raw_local_irq_restore(flags);
288343
oops_exit();
289344

345+
/* Executive summary in case the oops scrolled away */
346+
__show_regs(&exec_summary_regs, true);
347+
290348
if (!signr)
291349
return;
292350
if (in_interrupt())
@@ -305,10 +363,10 @@ NOKPROBE_SYMBOL(oops_end);
305363

306364
int __die(const char *str, struct pt_regs *regs, long err)
307365
{
308-
#ifdef CONFIG_X86_32
309-
unsigned short ss;
310-
unsigned long sp;
311-
#endif
366+
/* Save the regs of the first oops for the executive summary later. */
367+
if (!die_counter)
368+
exec_summary_regs = *regs;
369+
312370
printk(KERN_DEFAULT
313371
"%s: %04lx [#%d]%s%s%s%s%s\n", str, err & 0xffff, ++die_counter,
314372
IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "",
@@ -318,26 +376,13 @@ int __die(const char *str, struct pt_regs *regs, long err)
318376
IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION) ?
319377
(boot_cpu_has(X86_FEATURE_PTI) ? " PTI" : " NOPTI") : "");
320378

379+
show_regs(regs);
380+
print_modules();
381+
321382
if (notify_die(DIE_OOPS, str, regs, err,
322383
current->thread.trap_nr, SIGSEGV) == NOTIFY_STOP)
323384
return 1;
324385

325-
print_modules();
326-
show_regs(regs);
327-
#ifdef CONFIG_X86_32
328-
if (user_mode(regs)) {
329-
sp = regs->sp;
330-
ss = regs->ss;
331-
} else {
332-
sp = kernel_stack_pointer(regs);
333-
savesegment(ss, ss);
334-
}
335-
printk(KERN_EMERG "EIP: %pS SS:ESP: %04x:%08lx\n",
336-
(void *)regs->ip, ss, sp);
337-
#else
338-
/* Executive summary in case the oops scrolled away */
339-
printk(KERN_ALERT "RIP: %pS RSP: %016lx\n", (void *)regs->ip, regs->sp);
340-
#endif
341386
return 0;
342387
}
343388
NOKPROBE_SYMBOL(__die);
@@ -356,30 +401,9 @@ void die(const char *str, struct pt_regs *regs, long err)
356401
oops_end(flags, regs, sig);
357402
}
358403

359-
static int __init code_bytes_setup(char *s)
360-
{
361-
ssize_t ret;
362-
unsigned long val;
363-
364-
if (!s)
365-
return -EINVAL;
366-
367-
ret = kstrtoul(s, 0, &val);
368-
if (ret)
369-
return ret;
370-
371-
code_bytes = val;
372-
if (code_bytes > 8192)
373-
code_bytes = 8192;
374-
375-
return 1;
376-
}
377-
__setup("code_bytes=", code_bytes_setup);
378-
379404
void show_regs(struct pt_regs *regs)
380405
{
381406
bool all = true;
382-
int i;
383407

384408
show_regs_print_info(KERN_DEFAULT);
385409

@@ -389,36 +413,8 @@ void show_regs(struct pt_regs *regs)
389413
__show_regs(regs, all);
390414

391415
/*
392-
* When in-kernel, we also print out the stack and code at the
393-
* time of the fault..
416+
* When in-kernel, we also print out the stack at the time of the fault..
394417
*/
395-
if (!user_mode(regs)) {
396-
unsigned int code_prologue = code_bytes * 43 / 64;
397-
unsigned int code_len = code_bytes;
398-
unsigned char c;
399-
u8 *ip;
400-
418+
if (!user_mode(regs))
401419
show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT);
402-
403-
printk(KERN_DEFAULT "Code: ");
404-
405-
ip = (u8 *)regs->ip - code_prologue;
406-
if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
407-
/* try starting at IP */
408-
ip = (u8 *)regs->ip;
409-
code_len = code_len - code_prologue + 1;
410-
}
411-
for (i = 0; i < code_len; i++, ip++) {
412-
if (ip < (u8 *)PAGE_OFFSET ||
413-
probe_kernel_address(ip, c)) {
414-
pr_cont(" Bad RIP value.");
415-
break;
416-
}
417-
if (ip == (u8 *)regs->ip)
418-
pr_cont("<%02x> ", c);
419-
else
420-
pr_cont("%02x ", c);
421-
}
422-
}
423-
pr_cont("\n");
424420
}

arch/x86/kernel/process_32.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,14 @@ void __show_regs(struct pt_regs *regs, int all)
7676
savesegment(gs, gs);
7777
}
7878

79-
printk(KERN_DEFAULT "EIP: %pS\n", (void *)regs->ip);
80-
printk(KERN_DEFAULT "EFLAGS: %08lx CPU: %d\n", regs->flags,
81-
raw_smp_processor_id());
79+
show_ip(regs, KERN_DEFAULT);
8280

8381
printk(KERN_DEFAULT "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
8482
regs->ax, regs->bx, regs->cx, regs->dx);
8583
printk(KERN_DEFAULT "ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n",
8684
regs->si, regs->di, regs->bp, sp);
87-
printk(KERN_DEFAULT " DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n",
88-
(u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss);
85+
printk(KERN_DEFAULT "DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x EFLAGS: %08lx\n",
86+
(u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss, regs->flags);
8987

9088
if (!all)
9189
return;

arch/x86/mm/fault.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,20 +829,23 @@ static inline void
829829
show_signal_msg(struct pt_regs *regs, unsigned long error_code,
830830
unsigned long address, struct task_struct *tsk)
831831
{
832+
const char *loglvl = task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG;
833+
832834
if (!unhandled_signal(tsk, SIGSEGV))
833835
return;
834836

835837
if (!printk_ratelimit())
836838
return;
837839

838840
printk("%s%s[%d]: segfault at %lx ip %px sp %px error %lx",
839-
task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
840-
tsk->comm, task_pid_nr(tsk), address,
841+
loglvl, tsk->comm, task_pid_nr(tsk), address,
841842
(void *)regs->ip, (void *)regs->sp, error_code);
842843

843844
print_vma_addr(KERN_CONT " in ", regs->ip);
844845

845846
printk(KERN_CONT "\n");
847+
848+
show_opcodes((u8 *)regs->ip, loglvl);
846849
}
847850

848851
static void

0 commit comments

Comments
 (0)