Page 1 of 1

problem catching exception

Posted: Mon Nov 17, 2008 6:50 am
by fglasmachers
hello,
i am trying to set up c++ (with rtti and exceptions) in my kernel using libgcc and libsupc++.
I use gcc 4.1.2 to compile the kernel, grub to load it, and bochs to test it.
But i get an "invalid instruction" error after my test-exception is caught.

So i have stripped down the code to the very necessary to identify the problem.
It does the following:
  • provide a multibootheader
    include the eh_frame terminated by with a QUAD(0)
    provide debug output routine to protocol function calls
    provide a very simple heap

    disable interrupts
    setup stackpointer
    call __register_frame(start_eh_frame)
    call all ctors (there are no ctors but i wanted to be sure)
    call test-routine with try (throw) catch statement
Here is the full code listing:

startup.asm:

Code: Select all

global start_heap
global end_heap

global startup

global stderr
global __dso_handle

global __cxa_pure_virtual
global __cxa_atexit
global __cxa_finalize
global __cxa_guard_acquire
global __cxa_guard_release
global __cxa_guard_abort
global abort
global realloc
global strcpy
global strcat
global fputc
global fputs
global fwrite


; startup_cpp and fatal_error are provided by startup.cpp
extern startup_cpp
extern fatal_error


; setting up multiboot header constants
MODULEALIGN equ 1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ 1<<1                   ; provide memory map
FLAGS       equ MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ 0x1BADB002             ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)       ; checksum required


; section only containing multi boot header
section .multibootheader
align 4
  dd MAGIC
  dd FLAGS
  dd CHECKSUM


; bss section
section .bss

start_stack:
  resb 0x4000                 ; reserve 16K for stack
end_stack:

start_heap:
  resb 0x4000                 ; reserve 16K for heap
end_heap:


; text section
section .text
align 4

; basic starting point for bootloader to jump in
startup:
  cli                         ; disable interrupts
  cld                         ; gcc needs this

  mov esp, end_stack          ; setup the stackpointer

  call startup_cpp            ; jump into cpp-code

  hlt                         ; halt machine

; const variables with uninteresting value
stderr:
__dso_handle:

; functions that will never be called
__cxa_pure_virtual:
__cxa_atexit:
__cxa_finalize:
__cxa_guard_acquire:
__cxa_guard_release:
__cxa_guard_abort:
abort:
realloc:
strcpy:
strcat:
fputc:
fputs:
fwrite:
  call fatal_error            ; print debug msg an halt machine
  ret
startup.cpp:

Code: Select all

typedef unsigned int size_t;
void kmain();

// debug output
char* printDebugPos = reinterpret_cast<char*>(0xb8000);
void printDebug(const char* text)
{
  while (*text != 0)
  {
    if (printDebugPos >= (reinterpret_cast<char*>(0xb8000) + 2 * 80 * 24))
      break;
    *printDebugPos++ = *text++;
    *printDebugPos++ = 0x0f;
  }
}

extern "C"
{
  extern char start_heap, end_heap;
  extern char start_eh_frame;
  extern size_t start_ctors, end_ctors;
  void __register_frame(void*);

  void fatal_error()
  {
    printDebug("fatal error;");
    asm("hlt");
  }

  char* heap = &start_heap;

  void* malloc(size_t s)
  {
    printDebug("malloc;");
    if ((heap + s) >= &end_heap)
      fatal_error();
    void* m = heap;
    heap += s;
    return m;
  }
  void free(void*)
  {
    printDebug("free;");
  }
  void* memset(void* dest, int src, size_t size)
  {
    printDebug("memset;");
    for (size_t i = 0; i < size; ++i)
      reinterpret_cast<char*>(dest)[i] = src;
    return dest;
  }
  void* memcpy(void* dest, const void* src, size_t size)
  {
    printDebug("memcpy;");
    for (size_t i = 0; i < size; ++i)
      reinterpret_cast<char*>(dest)[i] = reinterpret_cast<const char*>(src)[i];
    return dest;
  }
  int n_strlen = 0;     // how often strlen is being called
  size_t strlen(const char* str)
  {
    if ((n_strlen++ % 1000) == 0)
    {
      printDebug("#strlen>");
      char c[6] = {'0' + (n_strlen / 1000) % 10, '0', '0', '0', ';', 0};
      printDebug(c);
    }
    size_t len = 0;
    while (*str != 0)
    {
      str++;
      len++;
    }
    return len;
  }
  int dl_iterate_phdr(unsigned long)
  {
    printDebug("dl_iterate_phdr;");
    return -1;
  }

  void startup_cpp()
  {
    __register_frame(&start_eh_frame);

    // call all the static constructors in the list.
    for (size_t* call = &start_ctors; call < &end_ctors; ++call)
    {
      printDebug("ctor;");
      ((void (*)(void))*call)();
    }

    // call kernel main
    kmain();
  }
}
test.cpp:

Code: Select all

void printDebug(const char*);

void kmain()
{
  try
  {
    printDebug("throw;");
    throw 8;
    printDebug("should_not_happen;");
  }
  catch (...)
  {
    printDebug("catch;");
  }
  printDebug("end;");
}
sections.ld:

Code: Select all

ENTRY (startup)

SECTIONS
{
  . = 0x00100000;

  .text :
  {
    *(.multibootheader)
    *(.text)
  }

  .rodata ALIGN (0x1000) :
  {
    *(.rodata)
  }

  .data ALIGN (0x1000) :
  {
    start_ctors = .;
    *(.ctors*)
    *(.ctor*)
    end_ctors = .;

    start_dtors = .;
    *(.dtor*)
    *(.dtors*)
    end_dtors = .;

    start_eh_frame = .;
    *(.eh_frame)
    QUAD(0)

    *(.data)
  }

  .bss :
  {
    sbss = .;
    *(COMMON)
    *(.bss)
    ebss = .;
  }
}
Makefile:

Code: Select all

all:
  nasm -f elf -o startup_asm.o startup.asm
  gcc -o startup_cpp.o -c startup.cpp -Wall -Wextra -fno-rtti -fno-exceptions 
  gcc -o test.o -c test.cpp -Wall -Wextra
  gcc -Tsections.ld -o kernel.bin startup_asm.o startup_cpp.o test.o -nostdlib -nostartfiles -nodefaultlibs -lsupc++ -lgcc_eh
And that is the output before bochs recognizes an invalid instruction:

Code: Select all

malloc;throw;malloc;memset;memset;memset;#strlen>0000;malloc;malloc;#strlen>1000;free;memcpy;memcpy;memset;memcpy;memset;memcpy;memset;memcpy;memset;#strlen>2000;memcpy;memcpy;memcpy;memcpy;catch;
I hope someone experienced in using libgcc and libsupc++ will be able to give me a hint what is going wrong.

Thx in advance

Re: problem catching exception

Posted: Tue Oct 13, 2009 2:56 am
by stealther
Hi. I'm running into similar problems.
Have you found any solution?

Re: problem catching exception

Posted: Tue Oct 13, 2009 8:20 am
by Creature
If I understand linker scripts correctly, I think you're having a duplicate instance of constructors and destructors here:

Code: Select all

    start_ctors = .;
    *(.ctors*)
    *(.ctor*)
    end_ctors = .;

    start_dtors = .;
    *(.dtor*)
    *(.dtors*)
    end_dtors = .;
  
   ...
AFAIK, '.ctor*' means: "Everything starting with '.ctor' should be put here.", so that includes everything starting with '.ctors' as well. Then you're putting pretty much the same again.

I'm not sure if this is your problem, but it seems incorrect to me nonetheless (then again, I never was too good with linker scripts, so if I made some terrible mistake, feel free to point out my mistake).