C++ Exception handling

Hi
I’m struggling to get C++ exception handling to work properly. I’m fairly certain it did work with a previous version of the tools/compiler. I’m currently using DS 2.3.2 with arm-non-eabi-g++ 4.7.4

Any exception that is thrown causes a crash with “Invalid memroy API call”. Sample code (from a very small sample project):

try
		{
			int i = 2;
			throw i;
			//throw logic_error("Test");
		}
		catch(int x)
		{

		}
		catch(exception &e)
		{
			//TRACE((1, e.what()));
		}
		catch(...)
		{

		}

Backtrace:

Invalid memory API call
	Unknown function (4e414)
	Unknown function (3196b)
	Unknown function (31a09)
	Unknown function (212595)
	Unknown function (1de01b)
	adlint_trcError+1c
	adlint_errHalt+46
	_sbrk+8
	_sbrk_r+10
	_malloc_r+21c
	_realloc_r+226
	realloc+10
	d_growable_string_callback_adapter+64
	d_print_subexpr+1ea

g++ is invoked with the following options:

-Os 
-g 
-ggdb3 
-Wall 
-c 
-fshort-enums 
-fdollars-in-identifiers 
-nostartfiles 
-fno-strict-aliasing 
-std=gnu++0x 
-Wno-write-strings  
-Wno-comment 
-Wno-psabi 
-fno-common 
-fomit-frame-pointer 
-mthumb-interwork 
-mthumb 
-march=armv5te 
-mapcs 
-mno-apcs-stack-check

Linker:

-Os -fshort-enums -fdollars-in-identifiers -ggdb3 -u _siwiStubsImplementation -nostartfiles -fomit-frame-pointer -mthumb-interwork -mthumb -march=armv5te -msoft-float -mfpu=fpa -mapcs -mno-apcs-stack-check -Wl,-nmagic -Wl,-Map,Firmware.map -laeabi -L"${dsAeabiStubPath}"

I’ve checked all the compiler and linker options and it looks fine, but I’m no expert on exceptions and stack unwinding etc.
Any ideas?

Not much idea on how exception handling works… but did u try just creating a sample project which uses only a simple try catch block? did u get the same error in this case also? what module? FW version?
what about just this piece of code ? (JUST CURIOUS). Not much expert on this… but we could move step by step to your issue… we should throw standard exception… may be some object of class… anyways i’ll look moer into this…

int main()
{
try
{
int i=9;
throw i;
}
catch(exception &e)
{
TRACE((1,e,what()));
}
return 0;
}

Just try this and see whether this also causes an exception… you can use AXF file in the backtraces window using browse option to get clear view on where the backtrace is coming.

What was the earlier C++ compiler version?

Thanks

I did test it with a very minimal app, derived from the standard helloworld sample. I did test it with throwing various things like ints etc.
The backtrace listed above was decoded using the axf file. I don’t know why it doesn’t show all the function names, as I switched on every bit of debugging and symbol info I could. I also linked with the debug libs.

I’ve upgraded to Develeoper Studio 3.0.0 and I’m using FW 7.52, so latest of everything. I will try to dig up an older version to see what it does.

I set the STL terminate handler using std::set_terminate to my own method. The termination handler is being called whenever you throw an exception.

I think there might be something missing in the linker script (gcc.lkopt). I don’t see .eh_frame in the text section. I did add it, but with no success, so there might be other parts missing as well.

It seems that the realloc implementation is the default one (calling sbrk), while when using C++ in Open AT, it is necessary to stub it in order to map it to adl_memXXX memory handling functions…
Is the stub file documented in Developer Studio online help (about C++) integrated in your application?

You are absoluterly right: My memory_stub.c is missing realloc, as does the one in the documentation. I don’t think that is the problem though. As mentioned in the previous post, I set my own termination handler, as I believe the realloc call occurs in the default newlib termination handler. My code now looks like this:

void Application::TerminationHandler()
{
	//static bool triedThrow = false;
	TRACE ((1, "Application::TerminationHandler"));
	adl_errHalt ( 999, "STL termination handler invoked." );

}

void Application::Main(adl_InitType_e initType)
{
	TRACE ((1, "[Application::Main] Start %d", initType));
	if(initType == adl_InitType_e::ADL_INIT_POWER_ON)
	{
		set_terminate(TerminationHandler);
		try
		{
			TRACE ((1, "[Application::Main] Throwing"));
			string s("Test");
			int i=3;
			throw i;
			//throw logic_error(s);
		}
		catch(int x)
		{
			TRACE ((1, "[Application::Main] Caught: %d", x));
		}
		catch(const::exception &e)
		{
			TRACE((1, e.what()));
		}
		catch(...)
		{

		}

	}
	TRACE ((1, "[Application::Main] Done %d", initType));

}

The backtrace now looks like this:

STL termination handler invoked.
	Unknown function (4e414)
	Unknown function (3196b)
	Unknown function (31a09)
	Unknown function (212595)
	Unknown function (1de01b)
	adlint_trcError+1c
	adl_errHalt+3e
	_ZN11Application18TerminationHandlerEv+18 (../src/Application.cpp:54)
	_ZN10__cxxabiv111__terminateEPFvvE+6
	_ZSt9terminatev+a
	__cxa_throw+5a
	_ZN11Application4MainE15_adl_InitType_e+76 (../src/Application.cpp:65)
	AppInitImpl+1e (../src/Application.cpp:95)
	main_task+6 (../src/Task.c:76)
	mos_headerInitTask+6c
	Unknown function (203a4)

If I look at the reasons given in all C++ documentation for this behavior, i.e. why an exception would not be caught, none of them apply.

The stack trace sounds more consistent now, but yes, still no explanation on why the exception is not caught.
When you say that you tried to modify the linker script, did you tried to do it in the build output directory?
Because since the linker script is generated by DS, your modifications have certainly be overridden at build time.
To address such use case, you should copy the linker script elsewhere (e.g. in the project root folder), and modify the linker command line to refer to it. Doing like this, you can safely give tries on the linker script…

I modified genlinkscript.py in the SiWi installation folder and verified that gcc.lkopt reflected the changes. I also tried adding --eh-frame-hdr and --eh-frame as XLinker options. After that, I did see a .eh_frame_hdr section in the map file, but it stil doesn’t work. I am definately a rookie with ARM linking, so I might be on the wrong track here.

I’ve attached my test app.
SiWiTest.zip (33.5 KB)

Thanks for having attached the app.
We’ll have a look to check if we can fix it; will keep you informed here as soon as we have something.

Any news on this?

Investigation on this topic is scheduled in the next release (DS 3.1; late July)
But as soon as we have a fix, I let you know

Please find attached a patched linker script generator which hopefully fixes the issue.
Unfortunately, this script only works with GCC 4.4, not with GCC 4.7 (which is installed by default with DS 3).
With GCC 4.7, apps generated with this script cause the device to crash.

To go back to GCC 4.4, you can configure the Preferences to point on a GCC 4.4 installation in a DS 2.3.2 installation tools folder.

This is just a workaround while waiting to find a solution also compatible with GCC 4.7
genlinkscript.py.zip (1.14 KB)

Thank you very much. I’ll test it on our code ASAP.

I’ve tested my code with both gcc 4.4.1 and 4.7.4 and my results are the same as yours.

Before receiving your patch, I made similar changes to the linker script, but with no success, as I was only using 4.7.4. I’ve tried various things, like moving the sections around, but nothing helps.

I see a few things that are different in the axf file (when dumped with readelf -a) when compiling with the different versions:

  1. The entry point address is different.
  2. The sizes of the AXF files are very different - 3Mb for 4.7.4 vs 6.8Mb for 4.4.1 for my sample app. This sounds like something important is missing from the 4.7.4 version, as I don’t think gcc has improved that much in terms of code size.
  3. There are no unwind sections in 4.4.1, but 4.7.4 has the following: Unwind table index ‘.ARM’ at offset 0x6914 contains 1906 entries. I don’t really understand how exception handling can work without the unwind sections.

I see ati9 returns incorrect info regarding the application. Is it because the oat_header is in an unexpected place?

The oat_header must be at the first place.
We’ve seen also that when the .ARM section is inserted between the header and the code, the ATI9 info were completely wrong. By moving the section elsewhere, the info are OK, but the app doesn’t start.
We probably need to go deeper into this. Meanwhile, you could keep on using GCC 4.4

I think I managed to fix the problem. Gcc 4.4.1 produces the .ARM section as type SHT_PROGBITS whereas 4.7.4 produces it as SHT_ARM_EXIDX. The output from readelf:

4.4.1:
[ 3] .ARM PROGBITS 002ca62c 02a6a4 019450 00 A 0 0 4
4.7.4
[ 3] .ARM ARM_EXIDX 002b928c 019304 00aeb0 00 AL 4 0 4

The problem is that elf.py only copies SHT_PROGBITS sections to the bin file. I’ve modified elf.py and elf_constants.py to include SHT_ARM_EXIDX and that seems to have solved the problem.

elf_constants.py: 121

#ARM specific
SHT_ARM_EXIDX=SHT_LOPROC+1  # ARM unwind section.

elf.py:283

if (section.sh_flags & SHF_ALLOC) and ((section.sh_type == SHT_PROGBITS) or (section.sh_type == SHT_ARM_EXIDX)):

Everything now works correctly, including exception handling.

I have one question: Why is only SHT_PROGBITS sections copied? This skips .bss and .uninit sections, which also require allocation. I’ve had many problems with static member variables and template instantiations that don’t get initialized. Could the missing .bss be the cause?
elfutils.zip (6.61 KB)

Wow, great job, thanks!
We’ll give a try and integrate your contribution ASAP, for sure.
bss section is initialized by the underlying FW at boot time, and uninit section… well… is not initialized, since the goal of this section is to be kept unchanged on a hot reset.
The issues concerning some missed initialization in C++ may be due to the particular nature of Open AT startup, where only the Open AT tasks entry points are called on app initialization. E.g. if you have declared global object instances, the constructors won’t be called; global instances need to be dynamically allocated.

Hi

Just following up on this issue.

I just upgraded to DS 3.3 and updated all packages, and it doesn’t look like this has been fixed yet.
Not a big issue - we just apply the fix manually each time we upgrade, but I am afraid that we may overwrite other important changes doing this.

Thanks

Hi,

yes indeed, the fix wasn’t integrated yet.
as you may have understood, since some months we are more and more focusing on Legato…
But it’s a poor excuse for such a tiny modification :wink:
I’m rising the priority of this topic in next release (DS 3.4), to avoid you applying the patch on each new release!

Thanks for your patience.

Hi,

unfortunately, we won’t be able to integrate this patch in official DS release, because it is actually breaking the ability to build a delta between two versions of an application binary, that we’re using to interwork with AirVantage platform. Looks like the delta engine doesn’t support binaries where some code is written out of PROGBITS sections.
We’ve done several tries, but we didn’t manage to have both features at the same time (we have either delta functional, or the C++ exceptions handling).

I’m afraid you’ll have to continue to apply the patch on every new DS release…
Sorry for that.