Welcome, Guest. Please login or register.

Author Topic: From low level exception handling to high level...  (Read 9880 times)

Description:

0 Members and 2 Guests are viewing this topic.

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
From low level exception handling to high level...
« on: October 22, 2006, 03:30:39 AM »
Hi,

I am wondering:

Is it possible to write a routine for Task->tc_TrapCode that can cause a second, higher level routine to be called upon return from the exception (rte) when the system is back in User mode?

What I basically want to do is to use low level traps to invoke higher level error handling such that handling a 680x0 trap, such as divide by zero has the overall effect of calling my high level function immediately after returning to User mode, rather than trying to do something about the error in the 680x0 trap code itself. The reason for this strange request is so that I can do something like this:

Code: [Select]

// hidden in some implementation file
void throwZeroDiv()
{
  throw ZeroDivide();
}

// in 'normal' code
int exampleFunction(int a, int b)
{
  return a/b;
}

//....

try {
  exampleFunction(2,0);
} catch (ZeroDivide& zd) {
  //...
}



Now, it would be easy to call throwZeroDiv() from within the 680x0 trap as set by Task->tc_TrapCode but, of course, the stack would be the SSP rather than the USP. Consequently, the supervisor stack would be unwound and I expect it would all go mental very quickly.

So, what I need to do is write a nice low level 'intermediate' handler in assembler (most likely) which would sneakily modify the stack of the user code to fool it into calling throwZeroDiv() upon executing the rte instruction.

I can see that this would need some careful manipulation of the stack frame. Has anybody actually done anything like this?

I realise that checking to see if an argument is zero and then throwing an appropriate exception directly would seem to be the easiest solution but what I am looking for is a 'zero touch' solution that doesn't require this and therefore doesn't affect the performance of 'good' code in normal operation.
int p; // A
 

Offline Piru

  • \' union select name,pwd--
  • Hero Member
  • *****
  • Join Date: Aug 2002
  • Posts: 6946
    • Show only replies by Piru
    • http://www.iki.fi/sintonen/
Re: From low level exception handling to high level...
« Reply #1 on: October 22, 2006, 03:42:33 AM »
I'm sorry but this is not going to work well. You have absolutely no way of knowing the state in which the system is when the exception occurs. It could well be inside some critical system semaphore being locked, and calling your high level function would just fubar the system.

The only semi-bulletproof way of doing this would be to have some signal or message being sent, and then the processing of the event would be delayed. This wouldn't be runtime processing of the exception though. And it still could be inside some critical system function where even Signal would be fatal.

One way of doing this would be to have some handler process around. At exception time a simple "frame" would be created, and it would be fed to the handler, and the offending task would be put to sleep. Then the handler would get the event, process it, and then (possibly) make the offending task continue. This would still nuke majorly if the offender is holding some semaphore the handler also wants. Exception handlers and high level functions just don't mix. Anyway, this method is what apps such as TNT and SmartCrash use. Check SmartCrash archive for source code.
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: From low level exception handling to high level...
« Reply #2 on: October 22, 2006, 04:06:43 AM »
@Piru

Cheers.

Do you ever sleep? :lol:

Quote
It could well be inside some critical system semaphore being locked, and calling your high level function would just fubar the system.


What if I could guarentee that the the tc_TrapCode would only be set to point to our "handler" above the implementation layer (by which I mean the place where all OS calls are made) ?
int p; // A
 

Offline Piru

  • \' union select name,pwd--
  • Hero Member
  • *****
  • Join Date: Aug 2002
  • Posts: 6946
    • Show only replies by Piru
    • http://www.iki.fi/sintonen/
Re: From low level exception handling to high level...
« Reply #3 on: October 22, 2006, 03:57:40 PM »
@Karlos

Quote
Do you ever sleep?

I do. Perhaps not at the same time as others. I also like blood, and... Ehh, wait, forget that.

Quote
What if I could guarentee that the the tc_TrapCode would only be set to point to our "handler" above the implementation layer (by which I mean the place where all OS calls are made) ?

If you mean that the tc_Trapcode would be set while executing your own code. Yes, that would work. But it could still get lockup condition, unless if you really isolate ALL OS call stuff from user code (for example no user code gets to run at all with any OS lock obtained). Also, you'd naturally still need to handle the case you get exception while doing something semi-critical in your own code... But at least that is easy to predict, unlike some OS related lock ups.
 

Offline motorollin

  • Hero Member
  • *****
  • Join Date: Nov 2005
  • Posts: 8669
    • Show only replies by motorollin
Re: From low level exception handling to high level...
« Reply #4 on: October 22, 2006, 04:35:17 PM »
How do you guys know all this stuff?  :-o

--
moto
Code: [Select]
10  IT\'S THE FINAL COUNTDOWN
20  FOR C = 1 TO 2
30     DA-NA-NAAAA-NAAAA DA-NA-NA-NA-NAAAA
40     DA-NA-NAAAA-NAAAA DA-NA-NA-NA-NA-NA-NAAAAA
50  NEXT C
60  NA-NA-NAAAA
70  NA-NA NA-NA-NA-NA-NAAAA NAAA-NAAAAAAAAAAA
80  GOTO 10
 

Offline Piru

  • \' union select name,pwd--
  • Hero Member
  • *****
  • Join Date: Aug 2002
  • Posts: 6946
    • Show only replies by Piru
    • http://www.iki.fi/sintonen/
Re: From low level exception handling to high level...
« Reply #5 on: October 22, 2006, 04:44:58 PM »
Quote
How do you guys know all this stuff?

20 years of hacking helps.
 

Offline motorollin

  • Hero Member
  • *****
  • Join Date: Nov 2005
  • Posts: 8669
    • Show only replies by motorollin
Re: From low level exception handling to high level...
« Reply #6 on: October 22, 2006, 04:52:55 PM »
I suppose that would be an advantage (and would mean I started hacking at age 3 :lol: )

--
moto
Code: [Select]
10  IT\'S THE FINAL COUNTDOWN
20  FOR C = 1 TO 2
30     DA-NA-NAAAA-NAAAA DA-NA-NA-NA-NAAAA
40     DA-NA-NAAAA-NAAAA DA-NA-NA-NA-NA-NA-NAAAAA
50  NEXT C
60  NA-NA-NAAAA
70  NA-NA NA-NA-NA-NA-NAAAA NAAA-NAAAAAAAAAAA
80  GOTO 10
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: From low level exception handling to high level...
« Reply #7 on: October 22, 2006, 05:13:28 PM »
@Piru

Quote
I do. Perhaps not at the same time as others. I also like blood, and... Ehh, wait, forget that.


A bit hypocritical of me, considering what time I posted. Surprised you never picked up on that :lol:

Quote

If you mean that the tc_Trapcode would be set while executing your own code. Yes, that would work. But it could still get lockup condition, unless if you really isolate ALL OS call stuff from user code (for example no user code gets to run at all with any OS lock obtained).


Well now, see that's the thing :-) The purpose of the implementation layer is to hide away the OS. It's even namespaced so that unless you are really trying to deliberately code at an OS level, you can't even reach it.

That said, OS Locks may be held within my code as a consequence of using synchronised calls (using the Lock class, for example). However, the destructor for this class guarentees that any lock it holds will be released.

Consider this:

Code: [Select]


class Lockable {
  // service class, wraps signalsemaphore
  // inherit this class to get threadsafe locking properties
  public:
    void waitLock(); // May throw ObjectDestroyed
    void freeLock();
    // ....other locking methods

  public:
    ~Lockable();

  private:
    OSNative::SignalSemaphore sem;
    bool destroyed;
  // guts...
};

void Lockable::waitLock()
{
  // if this throws ObjectDestroyed, it is considered
  // illegal for the caller to access the object again
  // in any way, shape or form.
  if (destroyed) {
    // too late
    throw ObjectDestroyed();
  }
  OSNative::ObtainSemaphore(&sem);
  if (destroyed) {
    // we got unlucky whilst waiting
    OSNative::releaseSemaphore(&sem);
    throw ObjectDestroyed();
  }
  // ok, we got the lock.
}

void Lockable::freeLock()
{
  if (!destroyed) {
    OSNative::ReleaseSemaphore(&sem);
  }
}

Lockable::~Lockable()
{
  OSNative::ObtainSemaphore(&sem);
  destroyed = true;
  OSNative::ReleaseSemaphore(&sem);

  // Any pending calls to waitLock() in concurrent threads
  // will now throw ObjectDestroyed() as soon as they obtain
  // the semaphore. Wait for the semaphore again before
  // exiting the destructor, to ensure this is the last
  // thread that will touch the object.

  OSNative::ObtainSemaphore(&sem);
  OSNative::ReleaseSemaphore(&sem);
}

// auxilliary class, used to make functions safely synchronizable:

class LockExclusive {
  public:
    LockExclusive(Lockable* obj) : item(obj) { item->waitLock(); }
    ~LockExclusive() { item->freeLock(); }

  private:
     Lockable* item;
};


The above is more or less the locking strategy my high level code uses.

To incorporate our handler, consider:

Code: [Select]

class MyClass : public Lockable {

  public:
   int riskyOperation(int a, int b);

  // ....

  private:
    int n;
}

int MyClass::riskyOperation(int a, int b)
{
  // Irrespective of how this function terminates, the
  // automatic instance of ExclusiveLock should be destroyed
  // correctly.

  LockEsclusive lock(this);
  n = a / b;
  return n;
}


This is the principle way I use locking in high level code. If something throws after 'lock' is instansiated, 'lock' itself will be destroyed and consequently will release the lock on the object it was called for (in this trivial case, our "MyClass" object).

So, if our a/b invokes our tc_TrapCode exception mechanism and 'appears' to magically throw ZeroDivide, our lock ought to be released cleanly.

Or at least, this is the idea.
int p; // A
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: From low level exception handling to high level...
« Reply #8 on: October 22, 2006, 05:20:58 PM »
Quote

motorollin wrote:
How do you guys know all this stuff?  :-o

--
moto


I don't, that's why I'm asking ;-)
int p; // A
 

Offline motorollin

  • Hero Member
  • *****
  • Join Date: Nov 2005
  • Posts: 8669
    • Show only replies by motorollin
Re: From low level exception handling to high level...
« Reply #9 on: October 22, 2006, 05:24:09 PM »
Quote
Karlos wrote:
Quote
motorollin wrote:
How do you guys know all this stuff?  :-o

I don't, that's why I'm asking ;-)

True. But you knew enough to ask the question in the first place  :idea:

--
moto
Code: [Select]
10  IT\'S THE FINAL COUNTDOWN
20  FOR C = 1 TO 2
30     DA-NA-NAAAA-NAAAA DA-NA-NA-NA-NAAAA
40     DA-NA-NAAAA-NAAAA DA-NA-NA-NA-NA-NA-NAAAAA
50  NEXT C
60  NA-NA-NAAAA
70  NA-NA NA-NA-NA-NA-NAAAA NAAA-NAAAAAAAAAAA
80  GOTO 10
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: From low level exception handling to high level...
« Reply #10 on: October 22, 2006, 05:40:57 PM »
That would be a side effect of my near schiziophrenic passion for both assembly language (low level) and C++ (other end of the scale)...

Anyway, getting back to the point, what I need to be able to do in order to at least test this idea is implement some sort of low level exception handler (in the 680x0 sense) that modifies the stackframe passed to it such that on executing the rte (return from exception) instruction, the flow of execution jumps into a function of my choosing, making it look as if the actual code went there all by itself. That function then throws the equivalent C++ exception that matches the class of error trapped by our low level code.

In other words, it would make the code appear to be modified as follows:

void throwZeroDivide()
{
  throw ZeroDivide();
}
//....

int n = a/b;

would, as if by magic, appear to be self-modifying, thus:

int n = throwZeroDivide(), a/b;

for the case where b was zero.

The issue is that I'm not sure how to modify the 680x0 stack frame in the low level handler to get the flow of execution to return to the 'wrong' place (throwZeroDivide()) and making it appear that the original code jumped there, rather than being shoved there by the trap.

Suggestions?
int p; // A
 

Offline Piru

  • \' union select name,pwd--
  • Hero Member
  • *****
  • Join Date: Aug 2002
  • Posts: 6946
    • Show only replies by Piru
    • http://www.iki.fi/sintonen/
Re: From low level exception handling to high level...
« Reply #11 on: October 22, 2006, 06:11:45 PM »
Quote
The issue is that I'm not sure how to modify the 680x0 stack frame in the low level handler to get the flow of execution to return to the 'wrong' place (throwZeroDivide()) and making it appear that the original code jumped there, rather than being shoved there by the trap.

You can't do that (easily). The problem is that you'd need something similar to setjmp/longjmp to restore the register and stack to known good situation. But if you do that, you again lose ability to return to previous context.

Making the code to return to some other location is trivial. Anything else gets VERY tricky.

If you intend to get this working you need assembly at both exception and the userlevel side.

Did you check the SmartCrash src? It doesn't divert the execution at all, though.
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: From low level exception handling to high level...
« Reply #12 on: October 22, 2006, 06:31:57 PM »
That's what I mean, really. Making it jump to some other place is not complicated, but preserving any contextual data is the hard part. Furthermore, the jump has to be executed immediately after we leave the supervisor state, which is slightly trickier than doing it whilst in the supervisor state - I can only assume this involves poking the stackframe.

Not checked the smart crash sources yet. I'm just working on this as a thought experiment for now :-)

-edit-

*sigh* It's been so long since I did any 'real' programming...
int p; // A
 

Offline Piru

  • \' union select name,pwd--
  • Hero Member
  • *****
  • Join Date: Aug 2002
  • Posts: 6946
    • Show only replies by Piru
    • http://www.iki.fi/sintonen/
Re: From low level exception handling to high level...
« Reply #13 on: October 22, 2006, 06:36:19 PM »
This works 010+ and up at least:
Code: [Select]

addq.l #4,sp         ; pop out the exception type in stack
move.l #myfunc,2(sp) ; new return address
rte

But that's it. There is NO context saving or restoring of any kind here. The execution continues at myfunc with the stack and register at the time of the exception.
 

Offline KarlosTopic starter

  • Sockologist
  • Global Moderator
  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 16879
  • Country: gb
  • Thanked: 5 times
    • Show only replies by Karlos
Re: From low level exception handling to high level...
« Reply #14 on: October 22, 2006, 06:59:47 PM »
Hmm, I think I got a bit confused: I was thinking that a7 was the SSP during the trap handling but your code implies it's the USP ?

int p; // A