pjc50
pjc50

Reputation: 1755

How exactly does the SEH (structured exception handling) mechanism work on ARM?

I'm aware of the general overview: http://msdn.microsoft.com/en-us/library/dn743843.aspx ; there is plenty of information available on the exception mechanism in win32 and some for win64, but ARM is not so clear.

I would like to know: when a machine exception is generated (memory protection violation), exactly where is control transferred to (so I can set a breakpoint)? Does it transfer into kernel mode? Presumably it looks up the address of a handler from the vector table and transfers there. Does the kernel code perform the SEH unwind, or is the unwind done in user mode? If in user mode, where exactly - part of the .exe, coredll, or elsewhere?

Background

We have a large application which places __try / __except handlers around the top level of each thread in order to log crashes in an informative way. This has worked fine for years on MIPS. We're now porting it to WinCE 7 on ARM, building under VS2008. We found that exceptions would sometimes not be handled properly in release builds. This seems to depend on exactly where in the code the exception is generated (I have a test function which deliberately accesses a NULL pointer to raise a SEH exception, and I'm calling this from various locations in the code).

We're using the /EHsc option to enable SEH. Release builds use /O2 /Os ; removing /O2 makes it work but significantly slower. That leads me to believe that the optimizer is deleting something that's required for SEH to work - but what exactly? Not all functions have the "mov r12, sp ..." prologue, but some that don't still work properly. Is it some error in the PDATA? Is there an upper limit on the number of PDATA entries? Is it normal that the output of "dumpbin /pdata" doesn't have a function name on every line?

By using the debugger (despite release build), I can place a breakpoint at the start of my exception filtering function (called from __except) and observe that it is never entered in the failure case. The program just exits.

Upvotes: 3

Views: 2784

Answers (4)

marcinj
marcinj

Reputation: 50026

This might be not the exact answer to your question but if your goal is to catch critical hardware exceptions and log its call stack then since Windowce CE 6.0 you can use AddVectoredExceptionHandler. In application I work (vs2005, ARM only) on I am able to get call stacks with the help of this function, also you dont need to add SEH catch/throw, exception handler will always get called.

Upvotes: 1

Ben Voigt
Ben Voigt

Reputation: 283793

We're using the /EHsc option to enable SEH

You're supposed to use /EHa with SEH. /EHsc allows the compiler to optimize away all exception-handling logic if it can prove that the C++ throw statement will not be reached inside the block.

Structured Exception Handling __try and __except will work even with /EHsc, but then C++ stack unwinding won't take place for structured exceptions. As a result, global state may be inconsistent after an exception, which may result in the failure of your handler.

Upvotes: 1

Igor Skochinsky
Igor Skochinsky

Reputation: 25318

I'm not familiar with the low-level kernel details, but pointer to the CRT exception handler is stored in the dwords just before the function:

0001106C     DCD _C_specific_handler  ; handler function
00011070     DCD _scope_table         ; scope table (__try/__except map)
00011074 start
00011074     MOV             R12, SP

Maybe put your breakpoint on the _C_specific_handler and see if it's invoked. The actual function is in COREDLL. If it does get called but does not pass the exception to your code, possibly the scope table info is wrong for some reason.

EDIT: the above applies to WinCE 6 and earlier. Since you mention VS2008, I'm pretty sure WinCE 7 still uses the same exception handling model. The link you mention (unwind code-based) applies to the new, ARMv7-only WinRT kernel (I think Windows Embedded Compact 2013 uses it too).

Upvotes: 1

Thomas Matthews
Thomas Matthews

Reputation: 57749

This is very easy. Type "ARM ARM" into your favorite search engine.

A narrower seach "ARM prefetch exception".

In essence, when the processor tries to fetch data from an undefined memory area, a Data Fetch exception is generated. The processor transfers execution to a predefined address called an exception vector. The rest of the behavior is up to the programmer or OS. For example, on an embedded system, a System Failure function may be called. On a desktop platform, the OS would generate a signal or exception and terminate the program.

Other platforms may have advanced Memory Management Unit (MMU) processors that can have fences. When a program accesses outside of the fenced area, an exeception or interrupt is generated. The region would be programmed into the MMU. This enable an OS to protect regions in memory that a User's program should not access, even though memory does exist.

Accessing undefined regions of memory or memory your program does not have access to is defined as "undefined behavior". This behavior may vary by compiler or platform and there is no standard behavior. Some platforms may generate "signals", others may generate "exceptions", some pass messages, others just crash. If you want portable memory exception handling, you will need to write platform specific code.

Upvotes: 0

Related Questions