C++ Exception Support: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
Line 2: Line 2:


=Introduction=
=Introduction=
In the ABI, C++ exception is supported by the cooperation of three layers. The first layer is the compiler. The compiler translates the "try" "catch" "throw" statements into calls to specific functions in C++ runtime. The second layer is the C++ runtime. For exception support this layer is more or less a wrapper around the third layer. All the dirty work are done by the third layer: the unwind library.
In the ABI, C++ exception is supported by the cooperation of three layers. The first layer is the compiler. The compiler translates the "try" "catch" "throw" statements into calls to specific functions in C++ runtime. The second layer is the C++ runtime. For exception support this layer is more or less a wrapper around the third layer. All the dirty work are done by the third layer: the unwind library. BTW, the second layer also adds support for RTTI & dynamic_cast


So, in order to add C++ exception support, you just need to port the second and the third layer into you kernel, which means you need to either port libsupc++ or port libcxxrt and an unwind library. I prefer the second option, because libsupc++ is part of GCC, it's even not trivial to compile it separately. For the unwind library part, you have two options, libunwind and libgcc_eh. libunwind is more difficult to port, it depends on pthread and some type definitions in <elf.h> <link.h> and <ucontext.h>. The major reason you may want libunwind instead of libgcc_eh is that libgcc_eh is licensed under LGPL.
So, in order to add C++ exception support, you just need to port the second and the third layer into you kernel, which means you need to either port libsupc++ or port libcxxrt and an unwind library. I prefer the second option, because libsupc++ is part of GCC, it's even not trivial to compile it separately. For the unwind library part, you have two options, libunwind and libgcc_eh. libunwind is more difficult to port, it depends on pthread and some type definitions in <elf.h> <link.h> and <ucontext.h>. The major reason you may want libunwind instead of libgcc_eh is that libgcc_eh is licensed under LGPL.

Revision as of 04:12, 12 July 2011

Under Linux, G++ implements the Itanium C++ ABI. If your kernel is compiled with a Linux compiler (either cross compiler or native compiler), you can follow this article and add C++ exception support to your kernel.

Introduction

In the ABI, C++ exception is supported by the cooperation of three layers. The first layer is the compiler. The compiler translates the "try" "catch" "throw" statements into calls to specific functions in C++ runtime. The second layer is the C++ runtime. For exception support this layer is more or less a wrapper around the third layer. All the dirty work are done by the third layer: the unwind library. BTW, the second layer also adds support for RTTI & dynamic_cast

So, in order to add C++ exception support, you just need to port the second and the third layer into you kernel, which means you need to either port libsupc++ or port libcxxrt and an unwind library. I prefer the second option, because libsupc++ is part of GCC, it's even not trivial to compile it separately. For the unwind library part, you have two options, libunwind and libgcc_eh. libunwind is more difficult to port, it depends on pthread and some type definitions in <elf.h> <link.h> and <ucontext.h>. The major reason you may want libunwind instead of libgcc_eh is that libgcc_eh is licensed under LGPL.

If you don't have any of the unix header files when compiling libcxxrt or libgcc_eh, e.g. <unistd.h>, create an empty one and try again. If you still can't compile libcxxrt or libgcc_eh, find a copy of the corresponding header file from a unix system and find out exactly what you are missing.

Port libcxxrt

libcxxrt can be downloaded here. libcxxrt is very easy to port, only 1 .c file and 8 .cc files. It depends on a few simple libc functions and a few pthread functions. If you don't need multi-thread support in your kernel (no smp, kernel code isn't preemptive), create some pthread stubs. Then copy all the source files and header files into your own kernel source tree and compile them with your existing build system, no special compiler flags are needed.

Port libgcc_eh

libgcc_eh is more difficult.

  1. First thing is to try to cross compile GCC (you will fail, don't worry).
  2. In the GCC build tree, you can find a directory named the same as your GCC target, e.g. i686-pc-linux-gnu.
  3. Change to the sub-directory libgcc under the directory mentioned above.
  4. Type command "make -k libgcc_eh.a" and record the console output of this command.
  5. Check the console output and find out the exactly source files and compiler flags needed for libgcc_eh
  6. Copy the source files into your kernel source tree, and add necessary compiler flags
  7. Try to compile these source files, any header file is missed, search GCC source tree and build tree for it.

There is a generated file "gthr-default.h", most of the time it contains only one line:

#include "gthr-posix.h"

. If you don't need multi-thread support, change it to

#include "gthr-single.h"

Function stubs

If you don't have some of the needed funtions, try some of these stubs

malloc

/* File scope static variable is slightly faster than function
 * scope static variable
 */
namespace { char* freeMemoryBase = SOME_BASE_ADDRESS; }
void* malloc(size_t size) {
	size = (size + 7) / 8 * 8;
	freeMemoryBase += size;
	return freeMemoryBase - size;
}

pthread

namespace { void* threadDataTable[64]; int freeEntry = 0;}
int pthread_key_create(pthread_key_t* key, void (*)(void*)) {
	assert(freeEntry < 64);

	*key = freeEntry;
	freeEntry++;
	return 0;
}

int pthread_once(pthread_once_t* control, void (*init)(void)) {
	if (*control == 0) {
		(*init)();
		*control = 1;
	}
	return 0;
}

void* pthread_getspecific(pthread_key_t key) {
	return threadDataTable[key];
}

int pthread_setspecific(pthread_key_t key, const void* data) {
	threadDataTable[key] = (void*)data;
	return 0;
}

int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t*) {
	*mutex = 0;
	return 0;
}

int pthread_mutex_lock(pthread_mutex_t* mutex) {
	assert(*mutex == 0);
	*mutex = 1;
	return 0;
}

int pthread_mutex_unlock(pthread_mutex_t* mutex) {
	assert(*mutex != 0);
	*mutex = 0;
	return 0;
}

int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*) {
	return 0;
}

int pthread_cond_signal(pthread_cond_t*) {
	return 0;
}

dladdr

This function is used for debugging by libcxxrt, not a big problem to simply return an error.

int dladdr(void*, Dl_info*) {
	return 0;
}

stdarg.h

This isn't a stub, indeed.

#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
#define va_copy(dest, src) __builtin_va_copy(dest, src)
typedef __builtin_va_list va_list;