This is the mail archive of the crossgcc@sources.redhat.com mailing list for the crossgcc project.

See the CrossGCC FAQ for lots more information.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: C++ exceptions and powerpc-unknown-eabi cross compilers


On Thursday 12 May 2005 6:54 pm, Richard Sewards wrote:
> Hi,
>
> Does support for C++ exceptions depend on the particular target GCC is
> built for?

there are two implementations of c++ exceptions, the setjump/longjump method,
--enable-sjlj-exceptions, which works on all platforms but is very slow and 
crap and the libunwind method which is much better, faster but only works on 
certain platforms.

there are many implications of c++ support, global constructors (are you using 
them ?), exception handling, rtti (for dynamic_cast) and implementing 
platform spesific locking in gcc.

i would *really* recomend that you dont use c++ exceptions in embedded 
projects, however i did, but its quite a lot of work.

i would recomend that you start by reading the code in gcc/unwind.XXX and 
preferably understanding it.

> I have been building gcc for powerpc-unknown-eabi for a while and I have
> been trying to get exceptions to work on our embedded target (which uses
> a commercial RTOS).  The compiler builds, my program builds and runs,
> but when it throws an exception it crashes inside the libgcc exception
> code and apparently it cannot find the exception frame.  Is exception
> handling disabled/unimplemented for powerpc-unknown-eabi targets, or
> should I look elsewhere for the problem.

i develop with an i486-unknown-elf target and a commerical RTOS (Nucleus in 
this case). Here are some pointers to what you need to do for c++ support.

1) for c++ exception support, using libunwind.

// header from gcc, that defines the dwarf2 object format used for stack 
unwinding
#include "unwind-dw2-fde.h"

#define EH_FRAME_SECTION_ASM_OP ".section\t.eh_frame,\"aw\""

// pointer to begin of .eh_frame section
extern "C" void * __eh_frame_start;

static struct object object;

// exception handling frame information
__register_frame_info ((void *)&__eh_frame_start, &object);

// test code
	try 
	{
		try
		{
			try
			{
				throw (float) 1.2;
			}
			catch (float)
			{
				throw (int) 1;
			}
		}
		catch (int)
		{
			throw;	
		}
		
	}
	catch (...)
	{
		double here;
	}


2) global constructors / destructors

/*
	gas code to access elf sections
*/
#define CTORS_SECTION_ASM_OP ".section\t.ctors,\"aw\""
#define DTORS_SECTION_ASM_OP ".section\t.dtors,\"aw\""

/*
	create a __CTOR_LIST__ from the section .dtors
*/
asm (CTORS_SECTION_ASM_OP);	/* cc1 doesn't know that we are switching! */
static void (* __CTOR_LIST__[1]) (void) __attribute__ ((section (".ctors"))) = 
{ (void (*) (void)) -1 };
asm (".previous"); /* go back */

/*
	create a __DTOR_LIST__ from the section .dtors
*/
asm (DTORS_SECTION_ASM_OP);	/* cc1 doesn't know that we are switching! */
static void (* __DTOR_LIST__[1]) (void) __attribute__ ((section (".dtors"))) = 
{ (void (*) (void)) -1 };
asm (".previous"); /* go back */

/*
run global destructors
*/
static void __attribute__((used))
do_global_dtors_aux (void)
{
	static func_ptr *p = __DTOR_LIST__ + 1;
	static bool completed =0;
	func_ptr f;

	if (completed)
    	return;

	while ((f = *p))
    {
      	p++;
      	f ();
    }
	
	// exception handling shutdown 
	__deregister_frame_info ((void *)&__eh_frame_start);

  	completed = 1;
}

/*
run global constructors
*/
static void __attribute__((used)) 
do_global_ctors_aux (void)
{
	static func_ptr *p = __CTOR_LIST__ + 1;
	static int completed = 0;

	if (completed)
    	return;
		
  	/*
  		mjf - now run through and find end marker (null)
  		and then execute the fuction pointers backwards, just like linux does :-)
  	*/
  	while ((*p) && (*p != (func_ptr)-1))
    {
    	p++;
    }

  	// there is a trailing null, so go back one
  	p--;

  	// run all the functions in the list, right back to the start of list 
(0xfffffff)
  	while (*p != (func_ptr)-1)
  	{
  		(*(p)) ();
		p--;
  	}

	static struct object object;

	// exception handling frame information
	__register_frame_info ((void *)&__eh_frame_start, &object);

  	completed = 1;
}

somepalce before main() call 
do_global_ctors_aux();


3) threading and locks, you can either patch gcc to support your platform, or 
use an exisiting target to enable your platform spesific threads/locking 
system for your RTOS, (this is required for c++ exceptions). Here is my 
example code.

--enable-threads=rtems

// will be 2, if task switching started
extern "C" INT INC_Initialize_State;

/* avoid depedency on rtems specific headers */
typedef void *__gthread_key_t;
typedef int   __gthread_once_t;
typedef void *__gthread_mutex_t;

/* 
mutex support 
*/

unsigned int rtems_gxx_mutex_lock_calls =0, 
			rtems_gxx_mutex_trylock_calls =0, 
			rtems_gxx_mutex_unlock_calls =0, 
			rtems_gxx_mutex_init_calls =0;

   
extern "C" void rtems_gxx_mutex_init(__gthread_mutex_t *mutex)
{
	rtems_gxx_mutex_init_calls++;
	
	*mutex = malloc(sizeof(NU_SEMAPHORE));
	
	NU_Create_Semaphore((NU_SEMAPHORE *)(*mutex), "GCC Sema", 1, NU_PRIORITY);
}

extern "C" int rtems_gxx_mutex_lock(__gthread_mutex_t *mutex)
{
	rtems_gxx_mutex_lock_calls++;
	int Status = -1;

	if (INC_Initialize_State == 2)
	{
    	if (NU_Obtain_Semaphore((NU_SEMAPHORE *)(*mutex), NU_SUSPEND) == 
NU_SUCCESS)
		{
			Status = 0;
		}
	}
	else
	{
		Status = 0;
	}
	
	return Status;
}

extern "C" int rtems_gxx_mutex_trylock(__gthread_mutex_t *mutex)
{
	rtems_gxx_mutex_trylock_calls++;
	int Status = -1;

	if (INC_Initialize_State == 2)
	{
		// Don't currently own the lock see whether it can be obtained 
		if (NU_Obtain_Semaphore((NU_SEMAPHORE *)(*mutex),NU_NO_SUSPEND) == 
NU_SUCCESS)
		{
			Status = 0;
		}
	}
	else
	{
		Status = 0;
	}
	
	return Status;
}

extern "C" int rtems_gxx_mutex_unlock(__gthread_mutex_t *mutex)
{
	rtems_gxx_mutex_unlock_calls++;
	int Status = -1;

	if (INC_Initialize_State == 2)
	{
    	if (NU_Release_Semaphore((NU_SEMAPHORE *)(*mutex)) == NU_SUCCESS)
		{
			Status = 0;
		}
	}
	else
	{
		Status = 0;
	}
	
	return Status;
}


//
// file i/o locking functions for newlib
//

/* avoid depedency on newlib specific headers */
typedef void * _LOCK_T;
typedef void * _LOCK_RECURSIVE_T;

//#define __LOCK_INIT(class,lock) class _LOCK_T lock;
//#define __LOCK_INIT_RECURSIVE(class,lock) class _LOCK_RECURSIVE_T lock;
/*
typedef struct stub_critical_section
{
	NU_SEMAPHORE CriticalSection;	// Locks the 
	int	count;
	NU_TASK * thread;
	
	critical_section()
	{
		CriticalSection.sm_id = 0;
		thread = NULL;
		count = 0;
	};
} CRITICAL_SECTION;
*/

// will be 2, if task switching started
// extern "C" INT INC_Initialize_State;


extern "C" void __local_lock_init(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		// only first time
		if (*((void **)lock) == 0)
		{
			*((void **)lock) = malloc(sizeof(CRITICAL_SECTION));
	
			SCXInitializeCriticalSection(*(CRITICAL_SECTION **)lock);
		}
	}
}
extern "C" void __local_lock_init_recursive(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		// only first time
		if (*((void **)lock) == 0)
		{	
			*((void **)lock) = malloc(sizeof(CRITICAL_SECTION));
			
			SCXInitializeCriticalSection(*(CRITICAL_SECTION **)lock);
		}
	}
}

extern "C" void __local_lock_close(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		SCXDeleteCriticalSection(*(CRITICAL_SECTION **)lock);
		
		// free when no longer held
		if ( (*(CRITICAL_SECTION **)lock)->count == 0)
		{
			free(*((void **)lock));
			*((void **)lock) = 0;
		}
	}
}
extern "C" void __local_lock_close_recursive(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		SCXDeleteCriticalSection(*(CRITICAL_SECTION **)lock);
		
		// free when no longer held
		if ( (*(CRITICAL_SECTION **)lock)->count == 0)
		{
			free(*((void **)lock));
			*((void **)lock) = 0;
		}
	}
}

extern "C" void __local_lock_acquire(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		SCXEnterCriticalSection(*(CRITICAL_SECTION **)lock);
	}
}
extern "C" void __local_lock_acquire_recursive(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		SCXEnterCriticalSection(*(CRITICAL_SECTION **)lock);
	}
}

/*
not used by newlib
extern "C" int __lock_try_acquire(_LOCK_T lock)
{
}
extern "C" int __lock_try_acquire_recursive(_LOCK_T lock)
{
}
*/

extern "C" void __local_lock_release(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		SCXLeaveCriticalSection(*(CRITICAL_SECTION **)lock);
	}
}
extern "C" void __local_lock_release_recursive(_LOCK_T lock)
{
	if (INC_Initialize_State == 2)
	{
		SCXLeaveCriticalSection(*(CRITICAL_SECTION **)lock);
	}
}


note the SCX?? calls allow reqursive calls, and the NU (Nucleus) ones dont.


> I did not use crosstool to build gcc, but I suspect that this list is
> the best place to ask.



regards
---
Matthew J Fletcher
Embedded Software
Serck Controls Ltd
---
**********************************************************************
Serck Controls Ltd, Rowley Drive, Coventry, CV3 4FH, UK
Tel: +44 (0) 24 7630 5050   Fax: +44 (0) 24 7630 2437
Web: www.serck-controls.com  Admin: post@serck-controls.co.uk
A subsidiary of Serck Controls Pty. Ltd. Reg. in England No. 4353634
**********************************************************************
This email and files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the above. Any views or opinions presented are those of the author
and do not necessarily represent those of Serck Controls Ltd.


This message has been checked by MessageLabs
******************************************************************

------
Want more information?  See the CrossGCC FAQ, http://www.objsw.com/CrossGCC/
Want to unsubscribe? Send a note to crossgcc-unsubscribe@sources.redhat.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]