sys7.1-doc-wip/ProcessMgr/Patches.c

428 lines
13 KiB
C

/*
File: Patches.c
Contains: Routines which monitor/maintain/switch patches made by applications.
Written by: Phil Goldman
Copyright: © 1986-1991 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<4> 1/14/91 DFH (JDR) Fixed latent bug in c_settrapaddress that mishandled
SetTrapWordAddress of am OSTrap.
<3> 1/10/91 DFH (JDR) Add hack to prevent HyperCard 1.2.5 patch to SysBeep.
<0> 10/26/86 PYG New Today.
*/
#include <errors.h>
#include <memory.h>
#include <menus.h>
#include <osutils.h>
#include <retrace.h>
#include <segload.h>
#include <traps.h>
#include <types.h>
#include <MFPrivate.h>
#include "Glue.h"
#include "Lomem.h"
#include "Data.h"
#include "SysMisc.h"
#include "Patches.h"
#define OS_TRAPTABLE_MASK ((short) (0x0FF))
#define TB_TRAPTABLE_MASK ((short) (0x3FF))
#define HIGHEST_CONTIG_MACPLUS_OSTRAPNUM ((short) (0x04F))
#define TRAPNUM_NEWOSSETTRAPADDRESS (_SetTrapAddress + (1<<9))
#define TRAPNUM_NEWTOOLSETTRAPADDRESS (_SetTrapAddress + (1<<9) + (1<<10))
#define TRAPNUM_SETTRAPWORDADDRESS (_SetTrapAddress + (1<<10))
#define SETTRAPWORDADDRESS_TYPE_MASK (0x0800)
#define NullTrapType 3
#define PATCH_TABLE_COUNT_INIT 10
Ptr blockTrapAddr;
/* c_GetProcessTrapAddress. Implements GetProcessTrapAddress selector of OSDispatch. */
pascal OSErr
c_GetProcessTrapAddress(ProcessSerialNumberPtr pPSN, Ptr *pTrapAddr, TrapType trapType, unsigned short trapNum)
{
register PEntryPtr pProc;
PatchEntryHdl hPatch;
PatchEntryPtr pPatch;
unsigned long olda5;
olda5 = ProcessMgrA5SimpleSetup();
if ((pProc = PEntryFromPSN(pPSN)) == nil)
{
A5SimpleRestore(olda5);
return(procNotFound);
}
/* If target is current process, a simple NGetTrapAddress will do */
if (pProc == pCurrentProcess)
{
*pTrapAddr = (Ptr) NGetTrapAddress(trapNum, trapType);
A5SimpleRestore(olda5);
return(noErr);
}
/* Otherwise, we must look through the process' patch registry */
hPatch = (*pProc->p_pcb)->applPatchTable;
trapNum &= (trapType == ToolTrap) ? TB_TRAPTABLE_MASK : OS_TRAPTABLE_MASK;
*pTrapAddr =
((pPatch = GetApplPatch(hPatch, trapNum, (unsigned short) trapType)) != nil)
? (Ptr) pPatch->newtrap
: GetRealTrapAddr(trapNum, trapType);
A5SimpleRestore(olda5);
return(noErr);
}
/* c_SetProcessTrapAddress. Implements SetProcessTrapAddress selector of OSDispatch. */
pascal OSErr
c_SetProcessTrapAddress(ProcessSerialNumberPtr pPSN, Ptr trapAddr, TrapType trapType, unsigned short trapNum)
{
PEntryPtr pProc;
unsigned long olda5;
olda5 = ProcessMgrA5SimpleSetup();
if ((pProc = PEntryFromPSN(pPSN)) == nil)
{
A5SimpleRestore(olda5);
return(procNotFound);
}
trapNum &= (trapType == ToolTrap) ? TB_TRAPTABLE_MASK : OS_TRAPTABLE_MASK;
UpdatePatchRegistry(pProc, trapAddr, trapType, trapNum);
if (pProc == pCurrentProcess)
settrapaddress_glue(trapAddr, trapNum, _SetTrapAddress, patchtraps[SETTRAPADDRESS].oldtrap);
A5SimpleRestore(olda5);
return(noErr);
}
/* c_settrapaddress. Patch to SetTrapAddress to record patches made by applications.
* App patches get a) removed on minor switch out or app death, b) re-implanted on
* minor switch in.
*
* Although SetTrapAddress is one trap, there are three variants based on bits 9 & 10
* of the trap word. Both off is the original SetTrapAddress, which is mostly out of
* date since the trap explosion in the Mac II resulted in an overlap between OS and
* toolbox trap numbers. The newer SetTrapAddresses cope by having the caller specify
* in the trap word (i.e. bits 9 & 10) whether the trap is an OS trap or a toolbox trap.
* Both on means ToolTrap. Bit 9 on and bit 10 off means OSTrap. Our table records
* both the trap number and trap type, so we can make the correct calls later on. The
* trap number is masked to the significant part so we can compare them.
* NOTE: This contains a hack to prevent HyperCard 1.2.5 from patching SysBeep. Said
* patch breaks horribly under System 7.0 and later. We go to pains to prevent just
* the first patch attempt, because XCMDs and sub-pieces may have their own patches.
*/
void
c_settrapaddress(Ptr trapAddr, unsigned short trapNum, short staTrapWord)
{
register unsigned short trapType, trapNumCopy;
unsigned long olda5;
olda5 = ProcessMgrA5SimpleSetup();
/* Assert this since we assume that masking to OS_TRAPTABLE_MASK after masking to
* TB_TRAPTABLE_MASK will not lose any bits from the OS_TRAPTABLE_MASK.
*/
assert((TB_TRAPTABLE_MASK & OS_TRAPTABLE_MASK) == OS_TRAPTABLE_MASK);
/* Assume TB until proven OS */
trapType = ToolTrap;
trapNumCopy = trapNum;
trapNum &= TB_TRAPTABLE_MASK;
/* Check whether trap is OSTrap. Inhibit HyperCard 1.2.5 SysBeep patch. */
if ( (staTrapWord == (short) TRAPNUM_NEWOSSETTRAPADDRESS) ||
((staTrapWord == (short) TRAPNUM_SETTRAPWORDADDRESS) && ((trapNumCopy & SETTRAPWORDADDRESS_TYPE_MASK) == 0)) ||
((staTrapWord == (short) _SetTrapAddress) &&
((trapNum <= HIGHEST_CONTIG_MACPLUS_OSTRAPNUM) ||
(trapNum == (_UprString & TB_TRAPTABLE_MASK)) ||
(trapNum == (_SetApplBase & TB_TRAPTABLE_MASK)))) )
{
trapType = OSTrap;
trapNum &= OS_TRAPTABLE_MASK;
}
else if ( (trapNum == (_SysBeep & TB_TRAPTABLE_MASK)) &&
(pCurrentProcess->p_preventFirstSysBeepPatch) )
{
pCurrentProcess->p_preventFirstSysBeepPatch = false;
A5SimpleRestore(olda5);
return;
}
/* Record it.
* NOTE: Gee, great… if pCurrentProcess is nil (like during switch_task) the trap
* doesn't get recorded. This means it becomes the "original" for new apps or any
* existing ones that haven't patched it. Gross. Hopefully, apps are the only
* things calling SetTrapAddress after INIT time.
*/
if (pCurrentProcess != nil)
UpdatePatchRegistry(pCurrentProcess, trapAddr, trapType, trapNum);
/* Call old routine */
settrapaddress_glue(trapAddr, trapNum, staTrapWord, patchtraps[SETTRAPADDRESS].oldtrap);
A5SimpleRestore(olda5);
}
/* GetApplPatch. Get ptr to application patch entry for given trapNum, or nil. */
PatchEntryPtr
GetApplPatch(PatchEntryHdl hPatch, register unsigned short trapNum, register unsigned short trapType)
{
register PatchEntryPtr pPatch;
PatchEntryPtr pPatchMax;
/* Get base and bound of table */
pPatch = (PatchEntryPtr) StripAddress(*hPatch);
pPatchMax = ((Ptr) pPatch + GetHandleSize(hPatch));
/* Search for a matching entry */
while (pPatch < pPatchMax)
{
if (pPatch->trapnum == trapNum && pPatch->traptype == trapType)
return(pPatch);
pPatch++;
}
/* exiting loop means no-find */
return(nil);
}
/* UpdatePatchRegistry. Add, remove, or change the patch registry entry for the
* given patch and process.
*/
void
UpdatePatchRegistry(PEntryPtr pProc, Ptr trapAddr, TrapType trapType, unsigned short trapNum)
{
PatchEntryHdl hPatch;
PatchEntryPtr pPatch;
PCB **pcbHdl;
Ptr currTrapAddr;
#ifdef WRITENOW_FIX
ProcPtr SetOverlaySystemEvent(ProcPtr);
#endif WRITENOW_FIX
#ifdef MSWORKS_FIX
ProcPtr SetOverlayRecoverHandle(ProcPtr);
#endif MSWORKS_FIX
/* Get handle to table and size.
* NOTE: The extraordinary case of a nil pProc->p_pcb happens if someone patches
* while pCurrentProcess == pNullProcess. Not impossible! An INIT can patch
* a trap we call while launching Finder. If that patch makes a patch, we're there!
*/
if ((pcbHdl = pProc->p_pcb) == nil)
return;
hPatch = (*pcbHdl)->applPatchTable;
/* See if app is modifying an existing patch (i.e. whether entry already exists). */
if ((pPatch = GetApplPatch(hPatch, trapNum, (unsigned short) trapType)) != nil)
{
/* Application is unpatching if new address same as its original */
if (pPatch->oldtrap == trapAddr)
{
RemoveApplPatch(pPatch);
(*pcbHdl)->applPatchCount--;
}
/* Application is re-adjusting a patch. Don't touch pPatch->oldtrap! */
else
pPatch->newtrap = trapAddr;
}
/* Application is making a new patch */
else
{
#ifdef WRITENOW_FIX
/* NOTE: Special hack for WriteNow -- see DeskMgrPatches.c */
if ( (trapNum == (_SystemEvent & TB_TRAPTABLE_MASK))
&& trapType == ToolTrap
&& pCurrentProcess->p_signature == 'nX^n' )
{
(ProcPtr)trapAddr = SetOverlaySystemEvent(trapAddr);
}
#endif WRITENOW_FIX
#ifdef MSWORKS_FIX
/* NOTE: Special hack for Works -- see MiscPatches.a and Utilities.c */
else if ( (trapNum == (_RecoverHandle & OS_TRAPTABLE_MASK))
&& (trapType == OSTrap)
&& (pCurrentProcess->p_signature == 'PSIP') )
{
(ProcPtr)trapAddr = SetOverlayRecoverHandle(trapAddr);
}
#endif MSWORKS_FIX
currTrapAddr = (pProc == pCurrentProcess)
? (Ptr) NGetTrapAddress(trapNum, trapType) : GetRealTrapAddr(trapNum, trapType);
AddApplPatch(hPatch, currTrapAddr, trapAddr, trapNum, trapType);
(*pcbHdl)->applPatchCount++;
}
}
/* AddApplPatch. Add trap to application's trap table */
void
AddApplPatch(PatchEntryHdl hPatch, Ptr currTrapAddr, Ptr trapAddr, unsigned short trapNum, unsigned short trapType)
{
PatchEntryPtr pPatch, pPatchMax;
u_long trapTabSize, newTrapTabSize;
/* Find size, base, and bound of trap table */
trapTabSize = GetHandleSize(hPatch);
pPatch = *hPatch;
pPatchMax = ((Ptr) pPatch + trapTabSize);
/* Find an empty entry */
while (pPatch < pPatchMax)
{
if (pPatch->traptype == NullTrapType)
break;
pPatch++;
}
/* Did we reach end of table? */
if (pPatch >= pPatchMax)
{
/* If so, grow by one entry. Reset pPatch since table handle might move. */
newTrapTabSize = trapTabSize + sizeof(PatchEntry);
SetHandleSize(hPatch, newTrapTabSize);
pPatch = (PatchEntryPtr) (((Ptr) *hPatch) + trapTabSize);
}
/* Initialize the patch entry. */
pPatch->trapnum = trapNum;
pPatch->traptype = trapType;
pPatch->oldtrap = currTrapAddr;
pPatch->newtrap = trapAddr;
}
/* GetRealTrapAddr. Get the trap address underneath the application patch level.
* NOTE: This does not work well if called while we are in the parts of switch_task
* where pCurrentProcess == nil, but either app's patches are still installed.
*/
Ptr
GetRealTrapAddr(unsigned short trapNum, unsigned short trapType)
{
register PatchEntryPtr pPatch;
register PatchEntryHdl hPatch;
pPatch = nil;
if (pCurrentProcess != nil)
{
hPatch = (*pCurrentProcess->p_pcb)->applPatchTable;
pPatch = GetApplPatch(hPatch, trapNum, (unsigned short) trapType);
}
return (pPatch != nil) ? (Ptr) pPatch->oldtrap : (Ptr) NGetTrapAddress(trapNum, trapType);
}
/* save_or_restore_dispatch. Switch in or out the patches made by the
* specified process. Patches should be switched out only after all the
* executable objects that need these patches have been switched out.
* Patches should be switched in before switching in any executable
* objects that need them. These executables include the VBLs, drivers,
* and "hooks".
*/
void
save_or_restore_dispatch(PCB *PCBPtr, int do_restore)
{
PatchEntryHdl hPatch;
register PatchEntryPtr pPatch, pPatchMax;
/* Check if valid */
if (PCBPtr == nil
|| PCBPtr->applPatchCount == 0
|| (hPatch = PCBPtr->applPatchTable) == nil)
return;
pPatch = *hPatch;
pPatchMax = ((Ptr) pPatch + GetHandleSize(hPatch));
while (pPatch < pPatchMax)
{
if (pPatch->traptype != NullTrapType)
{
/* (Un)patch this trap */
settrapaddress_glue(
(do_restore != SAVE_DISPATCH) ? pPatch->newtrap : pPatch->oldtrap,
pPatch->trapnum,
(pPatch->traptype ? TRAPNUM_NEWTOOLSETTRAPADDRESS : TRAPNUM_NEWOSSETTRAPADDRESS),
patchtraps[SETTRAPADDRESS].oldtrap);
}
pPatch++;
}
}
/* kill_dispatch. Remove app's patches, and the registry. */
void
kill_dispatch(PCB *PCBPtr)
{
if (PCBPtr->applPatchTable != nil)
{
save_or_restore_dispatch(PCBPtr, SAVE_DISPATCH);
PCBPtr->applPatchCount = 0;
DisposHandle(PCBPtr->applPatchTable);
PCBPtr->applPatchTable = nil;
}
}
/* InitApplPatchTable. Initialize specified trap table and counter. */
void
InitApplPatchTable(PatchEntryHdl *pApplPatchTab, short *applPatchCountPtr)
{
PatchEntryPtr pPatch, pPatchMax;
long size = PATCH_TABLE_COUNT_INIT * sizeof(PatchEntry);
PatchEntryHdl hPatch;
/* Initialize the parameters */
*applPatchCountPtr = 0;
*pApplPatchTab = hPatch = NewHandle(size);
if (hPatch == nil)
return;
/* Initialize the table */
pPatch = *hPatch;
pPatchMax = ((Ptr) pPatch + size);
while(pPatch < pPatchMax)
(pPatch++)->traptype = NullTrapType;
}
#pragma segment INIT
/* InstallPatches. Install the Process Mgr's patches. */
void
InstallPatches(void)
{
register PatchEntryPtr pPatch;
unsigned long isNewTrap;
Ptr unimplTrapAddr;
blockTrapAddr = (Ptr) NGetTrapAddress(_BlockMove, OSTrap);
unimplTrapAddr = (Ptr) NGetTrapAddress(_Unimplemented, ToolTrap);
pPatch = (IS_NUMAC_ROMS(ROM85)) ? &patchtraps[FIRST_ROM78_TRAP] : &patchtraps[FIRST_ROM75_TRAP];
while ( (unsigned long) pPatch->newtrap != 0)
{
isNewTrap = (long) pPatch->oldtrap; /* Marked by -1 in this field */
pPatch->oldtrap = (void (*)()) NGetTrapAddress(pPatch->trapnum, pPatch->traptype);
if (isNewTrap || pPatch->oldtrap != unimplTrapAddr)
{
NSetTrapAddress((long) ROUTINE_ADDR(pPatch->newtrap), pPatch->trapnum, pPatch->traptype);
}
pPatch++;
}
/* Cache address of CheckLoad */
SetOldCheckLoad(patchtraps[CHECKLOAD].oldtrap);
}
#pragma segment Main