[comp.sys.mac.programmer] How to do a LEGAL tail-patch

alexis@ccnysci.UUCP (Alexis Rosen) (02/15/89)

I've been thinking about this for a couple of days now. Here's my scheme.
Does anyone see any flaws?

Procedure MyPatch
SetTrapAddress to original trap address
Trap to original trap using A-line instruction
SetTrapAddress back to @MyPatch (&MyPatch for C people)
Fixup stack and return

It seems very simple. The only trick is that you have to store the original
trap address in code-relative space (stick it after the end of the patch code).
This is not a difficult thing to do, though it will break in a virtual-memory
system (by that point there will have to be legitimate ways to do this anyway).

So, what's the verdict?

Alexis Rosen
alexis@ccnysci.uucp

tim@hoptoad.uucp (Tim Maroney) (02/16/89)

In article <1272@ccnysci.UUCP> alexis@ccnysci.UUCP (Alexis Rosen) writes:
>
>I've been thinking about this for a couple of days now. Here's my scheme.
>Does anyone see any flaws?
>
>Procedure MyPatch
>SetTrapAddress to original trap address
>Trap to original trap using A-line instruction
>SetTrapAddress back to @MyPatch (&MyPatch for C people)
>Fixup stack and return

Nope, doesn't work.  Imagine that GetResource checks to see if it's
being called from GetFontInfo by looking at the return address.  The
reason you have to avoid tail patching is because if you just call the
old GetResource from inside your patch of GetResource, the return
address is inside your patch instead of inside GetFontInfo.

Your approach has exactly the same problem.  When your patch code calls
the trap, the return address is inside your code, not inside
GetFontInfo, the real caller.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"Its failings notwithstanding, there is much to be said in favor
 of journalism in that by giving us the opinion of the uneducated, it
 keeps us in touch with the ignorance of the community." -- Oscar Wilde

grg@berlin.acss.umn.edu (George Gonzalez) (02/16/89)

I've thought about this problem too.  Here's a scheme that might work.

	RealTrapAddr := GetTrapAddress( TheTrap );
	SetTrapAddress( TheTrap, OurHookProc );
	PokeLong( @MyTailPatch, SingleStepInterruptVector );

	proc OurHookProc;	; gets called by the trap dispatcher.
	bset	#TrapBit,XXX(SP)	; set single step trap bit in ret flags
	PushL	RealReapAddress		; a registerless "jump"
	RTS

	... the trap code runs, then when it returns, the CPU does a single
	step trap to your tail patch:

	proc	MyTailPatch
	... do my post processing...
	bclr	#TrapBit,YYY(SP)	; turn off single stepping
	RTE


Besides the obvious problem of being able to patch only one trap at a time,
and needing different XXX and YYY offsets for 68000 and 68020+, this idea
*might* just work.

fjo@ttrdf.UUCP (Frank Owen ) (02/17/89)

in article <1272@ccnysci.UUCP>, alexis@ccnysci.UUCP (Alexis Rosen) says:
> 
> Procedure MyPatch
> SetTrapAddress to original trap address
> Trap to original trap using A-line instruction
> SetTrapAddress back to @MyPatch (&MyPatch for C people)
> Fixup stack and return


Nope, Nope, Nope. This won't work. If the trap you are patching looks at
what routine has called it by examining the return address on the stack,
(Multifinder's GetNextEvent does this), it will appear as if only one 
routine (your patch) has ever called it. This may screw up the normal
functioning of the original trap. (It would screw up Multifinder's GNE,
for example.)


-- 
Frank Owen (fjo@ttrdf)  312-982-2182
AT&T Bell Laboratories 
5555 Touhy Ave., Skokie, IL  60077
PATH:  ...!att!ttrdf!fjo

holland@m2.csc.ti.com (Fred Hollander) (02/17/89)

In article <1272@ccnysci.UUCP> alexis@ccnysci.UUCP (Alexis Rosen) writes:
>
>I've been thinking about this for a couple of days now. Here's my scheme.
>Does anyone see any flaws?
>
>Procedure MyPatch
>SetTrapAddress to original trap address
>Trap to original trap using A-line instruction

Apple uses come-from patches to patch ROM in traps that are called by
buggy traps rather than replacing the entire trap.  They check the
return address to see if they should act.  The trap address doesn't
matter.  At this point, the patch will still see your code as the
return address and will not execute the bug fix.

>SetTrapAddress back to @MyPatch (&MyPatch for C people)
>Fixup stack and return

>So, what's the verdict?

A legal tail patch will check the return address and if in RAM, there
wouldn't be a come-from patch, so it's safe to execute your patch.  If
you just want to check the results of the trap, you can call the
original trap yourself, execute your code, then jump to the original
trap address, where it will now see the callers return address (in
ROM).  The only drawback is that the trap gets called twice which why
it's referred to as a come-again patch :).

>
>Alexis Rosen
>alexis@ccnysci.uucp

Fred Hollander
Computer Science Center
Texas Instruments, Inc.
hollander@ti.com

The above statements are my own and not representative of Texas Instruments.

tim@hoptoad.uucp (Tim Maroney) (02/17/89)

In article <293@berlin.acss.umn.edu> grg@berlin.acss.umn.edu (George
Gonzalez) suggests a legal way to do tail patching using single stepping.
It's very clever, and it gives me nightmares just thinking about it.

As George pointed out, only one trap can use this at a time.  That in
itself is enough to disqualify it, since the old trap code that you call
might itself call another patched trap.

What if a debugger is on?  That's bound to interfere with any software
use of the trace mode bit of the status register.

What about speed?  Trace mode is generally quite slow.

What about compatibility across processor lines?

It's one of the more intriguing suggestions I've heard here, but really,
it's too weird to go into production quality software.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"What's the ugliest part of your body?
 Some say your nose, some say your toes,
 But I think it's your mind."
-- Frank Zappa

amanda@lts.UUCP (Amanda Walker) (02/17/89)

grg@berlin.acss.umn.edu (George Gonzalez) writes:
    
    I've thought about this problem too.  Here's a scheme that might work.
    
    	RealTrapAddr := GetTrapAddress( TheTrap );
    	SetTrapAddress( TheTrap, OurHookProc );
    	PokeLong( @MyTailPatch, SingleStepInterruptVector );
    
    	proc OurHookProc;	; gets called by the trap dispatcher.
    	bset	#TrapBit,XXX(SP)	; set single step trap bit in ret flags
    	PushL	RealReapAddress		; a registerless "jump"
    	RTS
    
    Besides the obvious problem of being able to patch only one trap at a time,
    and needing different XXX and YYY offsets for 68000 and 68020+, this idea
    *might* just work.

It's a pretty clever idea, although it would be real annoying to try
and debug (since most debuggers like to use the Trace feature for,
well, tracing...).  You can solve the processor problem by checking
CPUflag when you install the patch.

This is probably unworkable in general, though, for two reasons: only
one patch can use the mechanism at a time, and it might not work in
environments where the processor is not running in supervisor mode,
such as A/UX, MacOS with a virtual memory INIT, the prophesied MacOS
rewrite, etc...

But definitely clever :-)...

-- 
Amanda Walker			...!uunet!lts!amanda / lts!amanda@uunet.uu.net
			  InterCon, 11732 Bowman Green Drive, Reston, VA 22090
--
Calm down; it's only ones and zeros...

pratt@boulder.Colorado.EDU (Jonathan Pratt) (02/18/89)

In article <293@berlin.acss.umn.edu> grg@berlin.acss.umn.edu (George Gonzalez) writes:
(about making legal tail patches)
>I've thought about this problem too.  Here's a scheme that might work.
>
>	RealTrapAddr := GetTrapAddress( TheTrap );
>	SetTrapAddress( TheTrap, OurHookProc );
>	PokeLong( @MyTailPatch, SingleStepInterruptVector );
>
>	proc OurHookProc;	; gets called by the trap dispatcher.
>	bset	#TrapBit,XXX(SP)	; set single step trap bit in ret flags
>	PushL	RealReapAddress		; a registerless "jump"
>	RTS
...
>	proc	MyTailPatch
>	... do my post processing...
>	bclr	#TrapBit,YYY(SP)	; turn off single stepping
>	RTE

Am I missing something?  This doesn't make sense to me.  RealTrapAddr is
set to the address of some trap routine, most likely one that obeys a
pascal calling convention.  There are no return flags once a trap routine
has been called.  At least in the Plus, the trap dispatcher disposes of
them immediately.  Am I wrong to think that this strategy hinges on
trap routines using RTE's, which of course they don't?

Jonathan

/* Jonathan Pratt          Internet: pratt@boulder.colorado.edu     *
 * Campus Box 525              uucp: ..!{ncar|nbires}!boulder!pratt *
 * University of Colorado                                           *
 * Boulder, CO 80309          Phone: (303) 492-4293                 */

brecher@well.UUCP (Steve Brecher) (02/18/89)

In article <293@berlin.acss.umn.edu>, grg@berlin.acss.umn.edu (George Gonzalez)
suggests a way to do non-interfering tail patches by setting the trace trap
bit in the PSW on the stack.

This won't work because the trap dispatcher discards the old PSW and returns
with an RTS -- it does not return with an RTE.
-- 

brecher@well.UUCP (Steve Brecher)

dwt@well.UUCP (dwt) (02/19/89)

There seems to be a lot of smoke about Tail patches but when it all
boils down, there is only one real answer.  If Apple does it, IN MY
OPINION, it must be possible, non-breakable in future and above all
sanctioned.
	So, if you can find out how Apple does it, understand how
it is being done and, MOST IMPORTANTLY, emulate their method EXACTLY,
you should be OK!
	To that end I would strongly advise people to purchase the
MacApp source listings to MacApp 2.0 and follow their nose.

NO GUARANTEES ON MY PART. THIS IS NOT AN OFFICIAL MESSAGE OF ANY 
SORT.  It appears a workable solution to me and whats more I
couldn't post it if I wanted to but check it out and maybe
this blather about No, it can't be done or Yes, it can be done
will subside to a dull roar.

Wow! Am I gald I got that off my chest. . .

-- 
David W. Taylor, Detail Software, San Anselmo, CA 94960 (415) 453-0712
                    {pacbell,lll-winken,hoptoad,hplabs,apple}!well!dwt