Page 1 of 1

Exception support in kernel.

Posted: Thu Jul 07, 2011 12:24 am
by torshie
Anyone succeeded in adding C++ exception support to his/her kernel? I tried to port libcxxrt(github.com/pathscale/). libcxxrt itself is very easy to port, it just need libc and a few simple pthread functions. But libcxxrt requires either libunwind or libgcc for stack unwinding. I tried to port libunwind, but found libunwind is horrible to port, it requires lots of posix functions. I haven't tried libgcc, not sure about it. Is libgcc easier to port?
I even tried to implement my own stack unwinding library, but gave up due to lacking of documents.
In case you are new to libcxxrt. libcxxrt is a library developed by pathscale, open sourced recently, distributed under a two-clause BSD license. It's ABI compatible with gcc, can be used to replace libsupc++.

Thanks
-torshie

Re: Exception support in kernel.

Posted: Thu Jul 07, 2011 1:56 am
by jnc100
There is a section at the bottom of OS_Specific_Toolchain which discusses getting libsupc++ and libgcc to work for exception handling in the kernel. Again, you have to provide a number of standard c library functions in the kernel as listed in the article (but they aren't too complicated) - the stream writing ones are just for debugging output - as well as declare certain objects in your source. Unfortunately it has been several years since I wrote that article so things may well have changed in more recent gcc versions. I haven't been working on a C++ kernel since then so don't know whether it still works. Hopefully there is someone else around with more recent experience.

Regards,
John.

Re: Exception support in kernel.

Posted: Thu Jul 07, 2011 9:10 am
by torshie
IIRC, llmv doesn't have a c++ runtime, the compiled binary needs g++ runtime to work.
EDIT: I agree with you that exception is never a must in kernel. But it's just cool to have exception support. Since we just writing our own kernels for fun, why not :mrgreen:

Re: Exception support in kernel.

Posted: Thu Jul 07, 2011 2:10 pm
by blobmiester
Yes, to an extent. I didn't port it though. I've implemented all required methods described in the Itanium C++ ABI [http://www.codesourcery.com/public/cxx-abi/abi.html]. That contains everything you need to know about implementing exception support (as well as typeid and dynamic_cast).

There are two important layers to implementing the exception specification [§4] (there is a third but that is the responsibility of the compiler). The first is the generic unwind interface (or Base) and the second is the C++ interface. The C++ interface is actually quite simple; it's just a few methods that wrap the unwind interface. The unwind interface is (of course) complex but the linked document above should provide sufficient information. If you need any specific help on implementing something in it I'd be glad to assist.

My advice would be to implement RTTI first, as it's miles easier (and required for proper exception handling). I would definitely advise against porting the GCC source. It's an incredible mess. Nearly a 1000 lines in the unwind personality function (madness). Of course they have to support a lot more architectures and environments than I do (but still).

Anyway, hope I helped a bit. Happy coding.

EDIT:

Oops. Forgot to actually say it: I've implemented all C++ runtime support (§18 of the C++ Standard) which includes typeid, exceptions, and dynamic_cast.

Re: Exception support in kernel.

Posted: Thu Jul 07, 2011 9:22 pm
by torshie
blobmiester wrote:Yes, to an extent. I didn't port it though. I've implemented all required methods described in the Itanium C++ ABI [http://www.codesourcery.com/public/cxx-abi/abi.html]. That contains everything you need to know about implementing exception support (as well as typeid and dynamic_cast).

There are two important layers to implementing the exception specification [§4] (there is a third but that is the responsibility of the compiler). The first is the generic unwind interface (or Base) and the second is the C++ interface. The C++ interface is actually quite simple; it's just a few methods that wrap the unwind interface. The unwind interface is (of course) complex but the linked document above should provide sufficient information. If you need any specific help on implementing something in it I'd be glad to assist.

My advice would be to implement RTTI first, as it's miles easier (and required for proper exception handling). I would definitely advise against porting the GCC source. It's an incredible mess. Nearly a 1000 lines in the unwind personality function (madness). Of course they have to support a lot more architectures and environments than I do (but still).

Anyway, hope I helped a bit. Happy coding.

EDIT:

Oops. Forgot to actually say it: I've implemented all C++ runtime support (§18 of the C++ Standard) which includes typeid, exceptions, and dynamic_cast.
You are my hero.
Yup, RTTI is really easy to implement, just implement a few trivial classes. I already implemented RTTI before I tried to to port libcxxrt. After porting libcxxrt, I deleted my implementation, because libcxxrt also implemented these classes. As you said, there are two layers to be implemented, for me the C++ interface is implemented in libcxxrt. The missing one is the unwind interface, that's why I want to port libunwind/libgcc. Last night I checked the source code of libgcc_eh.a. Compared with libunwind, libgcc is a true *monster*. So I have to either port libunwind or implement the unwind interface myself. I prefer the latter, because most of the sources are useless to me. I had a stub file to make the kernel compiles with exception enabled.

Code: Select all

#include <unwind.h>
#include <assert.h>

struct ProcessorContext {
	unsigned long genericRegister[16];
	unsigned long instructionPointer;
};

struct _Unwind_Context {
	ProcessorContext processorContext;
};

void _Unwind_Resume(struct _Unwind_Exception*) {
	assert(false);
}

_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception*) {
	assert(false);
	return _URC_FATAL_PHASE1_ERROR;
}

unsigned long _Unwind_GetTextRelBase(struct _Unwind_Context*) {
	return 0;
}

unsigned long _Unwind_GetDataRelBase(struct _Unwind_Context *) {
	return 0;
}

unsigned long _Unwind_GetLanguageSpecificData(struct _Unwind_Context*) {
	return 0;
}

unsigned long _Unwind_GetRegionStart(struct _Unwind_Context*) {
	return 0;
}

_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* e) {
	/* XXX Currently only C++ exception unwinding is supported */
	return _Unwind_RaiseException(e);
}

void _Unwind_SetGR(struct _Unwind_Context* context, int index,
		unsigned long value) {
	context->processorContext.genericRegister[index] = value;
}

void _Unwind_SetIP(struct _Unwind_Context* context, unsigned long value) {
	context->processorContext.instructionPointer = value;
}

_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void*) {
	/* XXX Implement this function.
	 *
	 * This function is mainly used for debugging, not a big problem to
	 * simply return an error.
	 */
	return _URC_FATAL_PHASE1_ERROR;
}

unsigned long _Unwind_GetIP(struct _Unwind_Context* context) {
	return context->processorContext.instructionPointer;
}
6 functions are left empty. But I think the 4 getters (_Unwind_GetTextRelBase, _Unwind_GetDataRelBase, _Unwind_GetLanguageSpecificData, _Unwind_GetRegionStart) should be easy to implement. After googling, I found that in order to implement function _Unwind_RaiseException, _Unwind_Resume I have to parse the .eh_frame .gcc_except_table .eh_frame_hdr elf sections to find out the exception handlers and the landing pad. So I need a dwarf2 parser.
Here are my questions:
1. Which information should be stored in the structure _Unwind_Context besides the CPU registers?
2. When and how should the structure _Unwind_Context be initialized?
3. Do I need to port a dwarf parser or I just need to get a copy of dwarf specification and craft my own simple dwarf parser?

BTW, would you mind send me a copy of your own unwind interface implementation?

Thanks in advance.
-torshie

Re: Exception support in kernel.

Posted: Fri Jul 08, 2011 10:36 am
by Owen
berkus wrote:
torshie wrote:IIRC, llmv doesn't have a c++ runtime, the compiled binary needs g++ runtime to work.
EDIT: I agree with you that exception is never a must in kernel. But it's just cool to have exception support. Since we just writing our own kernels for fun, why not :mrgreen:
http://llvm.org/docs/ExceptionHandling.html
That depends upon an external implementation of the Itanium C++ ABI. They have not yet reimplemented that.

Re: Exception support in kernel.

Posted: Sun Jul 10, 2011 1:58 am
by torshie
I got some progress on this :D

Good news:
1. Succeeded in compiling libgcc_eh.a. I extracted the libgcc_eh.a from the native gcc compiler and compiled the corresponding .c files in gcc source tree with my kernel compile system. No source file changes are needed.
2. The kernel compiles with exception support, and exceptions do get caught!
Bad news:
1. Re-throw doesn't work.
2. Exceptions are passed incorrectly.

In the following 3 code fragments, the first one works as expected, the second and the third don't.

Code: Select all

void foo() {
  throw 3;
}

void bar() {
  try {
    foo();
  } catch (...) {
    printf("Haha, got you!\n");
  }
}

Code: Select all

void bar() {
  try {
    foo();
  } catch (int& e) {
    printf("%d\n", e); // <======== Incorrect value 1023 printed.  *_* 
  }
}

Code: Select all

// Triple-fault, not haven't debugged, don't know why.
void bar() {
  try {
    foo();
  } catch (...) {
    printf("will re-throw\n");
    throw;
  }
}

void baz() {
  try {
    bar();
  } catch (...) {
    printf("Catch re-throw\n");
  }
}

Re: Exception support in kernel.

Posted: Sun Jul 10, 2011 2:07 am
by torshie
berkus wrote:Nice, did you inspect generated .eh_frames?
No, why do I need to?

Re: Exception support in kernel.

Posted: Sun Jul 10, 2011 9:04 am
by torshie
berkus wrote: Looks like stack unwinding doesn't work properly since you get the wrong values.
Got the problem, I made a stupid mistake in malloc() stub:

Code: Select all

static uintptr_t freeMemoryBase = &base;
void* malloc(size_t size) {
    size = Math::roundUp(size, 8);
    freeMemoryBase += size;
    return (void*)freeMemoryBase;
}
Re-throw still cause triple fault :(

Re: Exception support in kernel.

Posted: Mon Jul 11, 2011 10:22 am
by mehcode
Deleted

Re: Exception support in kernel.

Posted: Mon Jul 11, 2011 10:23 am
by blobmiester
Looking very nice torshie.
torshie wrote:Re-throw still cause triple fault :(
Remember to not call _Unwind_DeleteException on a rethrow because it would be deleted again (possibly the source of the triple fault).

If that doesn't work, let me see your __cxa_end_catch and __cxa_rethrow implementations.

Re: Exception support in kernel.

Posted: Wed Jul 13, 2011 12:12 am
by torshie
blobmiester wrote:Looking very nice torshie.
torshie wrote:Re-throw still cause triple fault :(
Remember to not call _Unwind_DeleteException on a rethrow because it would be deleted again (possibly the source of the triple fault).

If that doesn't work, let me see your __cxa_end_catch and __cxa_rethrow implementations.
I got the bug, __cxa_end_catch and __cxa_rethrow are OK, they are implemented in libcxxrt. The bug was in my elf loader, the kernel was incorrectly loaded.

Now I have fully supported C++ exceptions, and I also write a wiki page. http://wiki.osdev.org/C%2B%2B_Exception_Support

Thanks for all the replies.

Re: Exception support in kernel.

Posted: Wed Jul 13, 2011 1:16 pm
by TylerH
Why are you trying to port libgcc? If you build a cross-compiler, you can build libgcc for your target architecture in the process.

Re: Exception support in kernel.

Posted: Sat May 16, 2015 7:49 am
by demetrioussharpe
How do libcxxrt & libunwind actually tie into each other? I have done searches through the libcxxrt codebase to find instances where the unwind library functions are actually called & used, to no avail. Am I missing something?