From 2fb24921d889a3caa988905fdd1717139a831de5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 27 Dec 2009 02:36:38 +0100 Subject: [PATCH 1/3] Support for backtracing exceptions --- conf/i386.rmk | 6 ++ include/grub/backtrace.h | 25 +++++ kern/i386/realmode.S | 12 +++ lib/i386/backtrace.c | 194 +++++++++++++++++++++++++++++++++++++++ lib/i386/backtrace_int.S | 12 +++ 5 files changed, 249 insertions(+) create mode 100644 include/grub/backtrace.h create mode 100644 lib/i386/backtrace.c create mode 100644 lib/i386/backtrace_int.S diff --git a/conf/i386.rmk b/conf/i386.rmk index c3f036d0f..7031adee7 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -25,3 +25,9 @@ pkglib_MODULES += ata.mod ata_mod_SOURCES = disk/ata.c ata_mod_CFLAGS = $(COMMON_CFLAGS) ata_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += backtrace.mod +backtrace_mod_SOURCES = lib/i386/backtrace.c lib/i386/backtrace_int.S +backtrace_mod_CFLAGS = $(COMMON_CFLAGS) +backtrace_mod_ASFLAGS = $(COMMON_ASFLAGS) +backtrace_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/include/grub/backtrace.h b/include/grub/backtrace.h new file mode 100644 index 000000000..a0503b8b7 --- /dev/null +++ b/include/grub/backtrace.h @@ -0,0 +1,25 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_BACKTRACE_HEADER +#define GRUB_BACKTRACE_HEADER 1 + +void grub_backtrace (void); +void grub_backtrace_pointer (void *ptr); + +#endif diff --git a/kern/i386/realmode.S b/kern/i386/realmode.S index a74eb1217..2a371c581 100644 --- a/kern/i386/realmode.S +++ b/kern/i386/realmode.S @@ -108,6 +108,12 @@ gdt: gdtdesc: .word 0x27 /* limit */ .long gdt /* addr */ +realidt: + .word 0 + .long 0 +protidt: + .word 0 + .long 0 /* * These next two routines, "real_to_prot" and "prot_to_real" are structured @@ -159,6 +165,9 @@ protcseg: /* zero %eax */ xorl %eax, %eax + sidt realidt + lidt protidt + /* return on the old (or initialized) stack! */ ret @@ -166,6 +175,9 @@ prot_to_real: /* just in case, set GDT */ lgdt gdtdesc + sidt protidt + lidt realidt + /* save the protected mode stack */ movl %esp, %eax movl %eax, protstack diff --git a/lib/i386/backtrace.c b/lib/i386/backtrace.c new file mode 100644 index 000000000..06fb6c54c --- /dev/null +++ b/lib/i386/backtrace.c @@ -0,0 +1,194 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +struct idt_descriptor +{ + grub_uint16_t limit; + void *base; +} __attribute__ ((packed)); + +#define GRUB_IDT_ENTRY_TYPE_PRESENT 0x80 +#define GRUB_IDT_ENTRY_TYPE_NOT_PRESENT 0x00 +#define GRUB_IDT_ENTRY_TYPE_RING0 0x00 +#define GRUB_IDT_ENTRY_TYPE_TRAP32 0x0f + +struct idt_entry +{ + grub_uint16_t addr_low; + grub_uint16_t segment; + grub_uint8_t unused; + grub_uint8_t type; + grub_uint16_t addr_high; +} __attribute__ ((packed)); + +void +print_address (void *addr) +{ + const char *name; + int section; + grub_off_t off; + auto int hook (grub_dl_t mod); + int hook (grub_dl_t mod) + { + grub_dl_segment_t segment; + for (segment = mod->segment; segment; segment = segment->next) + if (segment->addr <= addr && (grub_uint8_t *) segment->addr + + segment->size > (grub_uint8_t *) addr) + { + name = mod->name; + section = segment->section; + off = (grub_uint8_t *) addr - (grub_uint8_t *) segment->addr; + return 1; + } + return 0; + } + + name = NULL; + grub_dl_iterate (hook); + if (name) + grub_printf ("%s.%x+%lx", name, section, (unsigned long) off); + else + grub_printf ("%p", addr); +} + +void +grub_backtrace_pointer (void *ebp) +{ + void *ptr, *nptr; + unsigned i; + + ptr = ebp; + while (1) + { + grub_printf ("%p: ", ptr); + print_address (*(void **) (ptr + sizeof (void *))); + grub_printf (" ("); + for (i = 0; i < 2; i++) + grub_printf ("%p,", *(void **) + (ptr + (i + 2) * sizeof (void *))); + grub_printf ("%p", *(void **) + (ptr + (i + 2) * sizeof (void *))); + grub_printf (")\n"); + nptr = *(void **)ptr; + if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME + || nptr == ptr) + { + grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); + break; + } + ptr = nptr; + } +} + +void +grub_interrupt_handler_real (void *ret, void *ebp) +{ + grub_printf ("Unhandled exception at "); + print_address (ret); + grub_printf ("\n"); + grub_backtrace_pointer (ebp); + grub_abort (); +} + + +void +grub_backtrace (void) +{ +#ifdef __x86_64__ + asm volatile ("movq %rbp, %rdi\n" + "call grub_backtrace_pointer"); +#else + asm volatile ("movl %ebp, %eax\n" + "call grub_backtrace_pointer"); +#endif +} + +static grub_err_t +grub_cmd_backtrace (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_backtrace (); + return 0; +} + +#define NUM_INTERRUPTS 0x20 + +void grub_int_handler (void); + +struct idt_descriptor idt_desc; + +static grub_err_t +setup_interrupts (void) +{ + struct idt_entry *idt_table; + unsigned long i; + grub_uint16_t seg; + idt_table = grub_memalign (0x10, NUM_INTERRUPTS * sizeof (struct idt_entry)); + if (!idt_table) + return grub_errno; + idt_desc.base = idt_table; + idt_desc.limit = NUM_INTERRUPTS; + + asm volatile ("xorl %%eax, %%eax\n" + "mov %%cs, %%ax\n" :"=a" (seg)); + + for (i = 0; i < NUM_INTERRUPTS; i++) + { + idt_table[i].addr_low = ((grub_addr_t) grub_int_handler) & 0xffff; + idt_table[i].addr_high = ((grub_addr_t) grub_int_handler) >> 16; + idt_table[i].unused = 0; + idt_table[i].segment = seg; + idt_table[i].type = GRUB_IDT_ENTRY_TYPE_PRESENT + | GRUB_IDT_ENTRY_TYPE_RING0 | GRUB_IDT_ENTRY_TYPE_TRAP32; + } + + asm volatile ("lidt %0" : : "m" (idt_desc)); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(backtrace) +{ + grub_err_t err; + err = setup_interrupts(); + if (err) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + cmd = grub_register_command ("backtrace", grub_cmd_backtrace, + 0, "Print backtrace."); +} + +GRUB_MOD_FINI(backtrace) +{ + grub_unregister_command (cmd); +} diff --git a/lib/i386/backtrace_int.S b/lib/i386/backtrace_int.S new file mode 100644 index 000000000..a51fc5ee4 --- /dev/null +++ b/lib/i386/backtrace_int.S @@ -0,0 +1,12 @@ +#include + + .text + .p2align 4 + +FUNCTION(grub_int_handler) + /* FIXME: push this only when CPU pushes no error on stack. */ + push $0 + pusha + movl 36(%esp), %eax + movl %ebp, %edx + call EXT_C(grub_interrupt_handler_real) From 5dfadcb34b448e9b767f300f8d7e34b8aaf3d23b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 30 Dec 2009 18:11:43 +0100 Subject: [PATCH 2/3] Fix warnings --- lib/i386/backtrace.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/i386/backtrace.c b/lib/i386/backtrace.c index 06fb6c54c..144b1da97 100644 --- a/lib/i386/backtrace.c +++ b/lib/i386/backtrace.c @@ -46,7 +46,9 @@ struct idt_entry grub_uint16_t addr_high; } __attribute__ ((packed)); -void +void grub_interrupt_handler_real (void *ret, void *ebp); + +static void print_address (void *addr) { const char *name; @@ -86,14 +88,11 @@ grub_backtrace_pointer (void *ebp) while (1) { grub_printf ("%p: ", ptr); - print_address (*(void **) (ptr + sizeof (void *))); + print_address (((void **) ptr)[1]); grub_printf (" ("); for (i = 0; i < 2; i++) - grub_printf ("%p,", *(void **) - (ptr + (i + 2) * sizeof (void *))); - grub_printf ("%p", *(void **) - (ptr + (i + 2) * sizeof (void *))); - grub_printf (")\n"); + grub_printf ("%p,", ((void **)ptr) [i + 2]); + grub_printf ("%p)\n", ((void **)ptr) [i + 2]); nptr = *(void **)ptr; if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME || nptr == ptr) From c1677cfccf45652833f4906d3361b037acfa2a91 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 14 Nov 2011 14:27:52 +0100 Subject: [PATCH 3/3] Fix a mismerge --- grub-core/Makefile.core.def | 5 +++-- grub-core/lib/i386/backtrace.c | 25 +++++++++---------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 003041ff6..cc1d4702d 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1711,8 +1711,9 @@ module = { module = { name = backtrace; - common = lib/i386/backtrace.c lib/i386/backtrace_int.S; - enable = i386; + common = lib/i386/backtrace.c; + common = lib/i386/backtrace_int.S; + enable = x86; }; module = { diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c index 144b1da97..42b9f9e4c 100644 --- a/grub-core/lib/i386/backtrace.c +++ b/grub-core/lib/i386/backtrace.c @@ -26,6 +26,8 @@ #define MAX_STACK_FRAME 102400 +GRUB_MOD_LICENSE ("GPLv3+"); + struct idt_descriptor { grub_uint16_t limit; @@ -51,31 +53,22 @@ void grub_interrupt_handler_real (void *ret, void *ebp); static void print_address (void *addr) { - const char *name; - int section; - grub_off_t off; - auto int hook (grub_dl_t mod); - int hook (grub_dl_t mod) + grub_dl_t mod; + + FOR_DL_MODULES (mod) { grub_dl_segment_t segment; for (segment = mod->segment; segment; segment = segment->next) if (segment->addr <= addr && (grub_uint8_t *) segment->addr + segment->size > (grub_uint8_t *) addr) { - name = mod->name; - section = segment->section; - off = (grub_uint8_t *) addr - (grub_uint8_t *) segment->addr; - return 1; + grub_printf ("%s.%x+%" PRIxGRUB_SIZE, mod->name, segment->section, + (grub_uint8_t *) addr - (grub_uint8_t *) segment->addr); + return; } - return 0; } - name = NULL; - grub_dl_iterate (hook); - if (name) - grub_printf ("%s.%x+%lx", name, section, (unsigned long) off); - else - grub_printf ("%p", addr); + grub_printf ("%p", addr); } void