Table of Contents
Library purpose
YAHM library was designed for simple migration from ARM-based hack to standalone application. With help of YAHMlib you can easily implement syscall patching inside your application.
Library review
YAHMLib has 3 layers of API. All API's are implemented as m68k functions.
- High layer API is the easiest one. With this API YAHMLib consider your application as hack. API allow you to activate and deactivate patches.
- Middle layer API. This layer can be used for manual syscall patching.
- Low layer API. This layer implements ARM versions of SysGetTrapAddress and SysSetTrapAddress functions.
The benefits of YAHMLib
- Simple application structure is similar to classic hack database layout.
- Safe trap chain handling. YAHMLib carefully works with trap chain. If you want to deactivate hack from middle of this chain, you can be sure that chain remain unbroken.
- Processor data and instruction cache issues are solved automagically.
- Thumb-awareness support. You can compile patches as pure ARM code. Library wrap your code with thumb-aware thunks.
- GCC .got sections are processed automatically.
Current documentation assumes that reader already knows how to create hack. Only YAHMLib API is duscussed below.
Library integration
YAHM library can be used with both CW and GCC compilers. Just include lowlevel.c and trapcontrol5.c sources to your project. yahm_lib.h header contains all YAHM API functions. Include 5 arm resources into target executable.
Also you can include YahmLibrary.rcp into your project:
#include "yahm_int.h" DATA "armc" ID YAHM_INIT_RES_ID "armc270E.bin" DATA "armc" ID YAHM_SET_TRAP_RES_ID "armc270F.bin" DATA "armc" ID YAHM_FAT_THUNK_RES_ID "fatthunk.bin" DATA "armc" ID YAHM_SHORT_RES_ID "SHORT.BIN" DATA "armc" ID YAHM_SHORT_OLD_RES_ID "shorttoold.bin" DATA "armc" ID YAHM_CW_THUNK_RES_ID "cwthunk.bin"
Error codes
YAHM library return some custom errors:
- hackErrWrongTrapInfo - returned if TRA5 resource wrong
- hackErrNoFreeThunk - library can't find free thunk for patch. Increase thunkCount value and restart device.
- hackErrNoActiveApp - library can't detect current application. Avoid trap installation from callback-based notification handlers.
- hackErrInitializationFailed - hack initialization function (resource armc 999) returns false.
- hackErrNoHackResources - current app have no patch resources. Check that all 'armc' and 'TRA5' are added to executable.
- hackErrNoLibraryArmlet - library can't find it's own armlets. Include it into application.
High level API
High level API is the simplest one. Just move all resources from hack to standalone application. API contains of two functions: one for hack activation and one for hack deactivation.
Err YAHM_InstallHack(void); Err YAHM_UninstallHack(void);
YAHM_InstallHack install all 'armc' resources from current application. Alse initialization 'armc' 999 resource is called before if exists.
YAHM_UninstallHack uninstall patches that were installed with YAHM_InstallHack.
Known issues for hack to application migration: Check type of application in hack code. Now it's 'appl', not the 'code'. Implement configuration screen manually. It's much simple than writing a hack configuration panel.
Middle level API
Use middle level instead of high level if you want flexible trap patching. High level call middle level functions for each 'armc' resource.
Err YAHM_ExecuteInitialization(void *initCodeResource, Boolean init); // initCodeResource - pointer to initialization arm code // init - true for initialization, false for deinitialization Err YAHM_InstallTrap(MemHandle hTrapCode, MemHandle hGot, MemHandle hTrapInfo, UInt32 creator, UInt16 resId); // hTrap code - handle for trap arm code chunk (or resource) // hGot - handle for gcc GOT section, pass NULL for CodeWarrior // hTrapInfo - handle for TRA5 resource with YAHM_SyscallInfo5 structure // creator, resId - those parameters are used for FtrSet(creator, resId, oldTrapAddress) void YAHM_UninstallTrap(MemHandle hTrap, UInt32 creator, UInt16 resID); // hTrap code - handle for trap arm code chunk (or resource) // creator, resId - those parameters are used for restoring old address from FtrGet(creator, resId, &oldTrapAddress) void *YAHM_FixupGccCode(MemHandle hGot, void *pCodeResource, void* *ppGotPtr); void *YAHM_FixupGccCodeEx(MemHandle hGot, MemHandle hCodeResource, void* *ppGotPtr); // pCodeResource - pointer to arm code chunk // hCodeResource - pointer to arm code chunk // hGotResource - handle for gcc GOT section, pass NULL for CodeWarrior // ppGotPtr - pointer to GOT into relocated chunk // return value - pointer to relocated chunk or codeResource if GOT section is absent void YAHM_FreeRelocatedChunk(void *pRelocatedCode);
YAHM_ExecuteInitialization used for calling initialization/deinitialization resouce manually.
Call YAHM_InstallTrap to install trap. Pass handles for code, .got, trap info for proper installation. Function save old syscall pointer to feature with creator and resId parameters.
Call YAHM_UninstallTrap to uninstall trap.
.got section
Compilation with arm-gcc creates ELF executable file and extracts code section from it. Creating position-independent code (PIC) in ELF executable creates additional .got section. Code uses .got section to evaluate function addresses and literal strings offsets. build-prc from YAHM SDK includes .got section into target .prc file with the same ResID as appropriate armc resource has. YAHM loads both armc and .got and merge it into single resource in dynamic heap. High and Middle levels load .got section too. YAHM_FixupGccCode and YAHM_FixupGccCodeEx function can be used to load resource with .got section manually. Those functions merge code resource (passed in hCodeResorce or pCodeResource parameters) and .got resource (passed in hGotResource parameter). Function returns an address of relocated chunk in dynamic memory or NULL if there are no enough memory. ppGotPtr parameter points to .got section on exit. This parameter value can be used to set R10 register for PIC arm code.
YAHM_FreeRelocatedChunk can be used for freeing relocated code chunk.
hGotResource parameter can be set to NULL. In this case functions don't make relocation and return pCodeResource as relocated address value. If relocated address is found in storage memory, then YAHM_FreeRelocatedChunk doesn't free chunk. This feature can be used to load and relocate both GCC and CW resources similary.
YAHMLib v1.07+ checks for NVFS manager existence and copies all code resources into dynamic heap to avoid problems with NVFS DBCache.
Low level API
Low level API mimics pre-OS5 API. Only two functions for setting and retrieving trap handler address. Those functions require deep knowledge of ARM programming.
void *YAHM_GetTrapAddress(UInt32 base, UInt32 offset); void *YAHM_SetTrapAddress(UInt32 base, UInt32 offset, void *trapHandler);
User-defined functions
YAHMLib requires few user-defined functions to work. Developer should implement those simple functions manually. The simplest version are showed below.
Persistent settings
typedef struct{ // Global YAHM settings. Should be saved in preferences. UInt32 protectYAHM; // if true, then YAHM protect application database UInt32 thunkCount; // number of trap slots. allocate at least 40-50 slots. }YAHM_persistSettings; // copy persist settings to structure, allocated by YAHM extern void YAHM_GetPersistSettings(YAHM_persistSettings *pSettings); // implementation sample void YAHM_GetPersistSettings(YAHM_persistSettings *pSettings){ pSettings->protectYAHM = true; pSettings->thunkCount = 40; }
YAHMLibrary require persistent setting to work. Developer should implement YAHM_GetPersistSettings function for changing library behaviour. Those settings can be either set in code or can be changed by user.
- protectYAHM - if this field set to non-zero, library protect current application against deletion when traps are set. Current application checks with SysCurAppDatabase syscall. Only YAHM_InstallHack and YAHM_UninstallHack function use this value.
- thunkCount. This field is used for thunk array allocation. YAHM allocates one thunk per active syscall patch. Usually patch deactivation frees thunk, but sometimes patch deactivation require leaving thunk in “used” state to support patch linked list integrity. If your code require reset for deactivation, you can make thunkCount equal to number of patched traps. Else, especially if EvtGetEvent is patched or your app deactivates on each hotsync, allocate 40-50 thunks.
Runtime settings
typedef struct{ // Runtime YAHM settings. They are valid on single YAHM execution. Should be saved in feature pointer UInt32 activeHacksCount; void *pPool; }YAHM_runtimeSettings; extern void YAHM_SetRuntimeSettings(YAHM_runtimeSettings *pSettings); // return ptr to runtime settings structure. extern YAHM_runtimeSettings *YAHM_GetRuntimeSettingsPtr(void); // callback function. extern void YAHM_warnAboutIncompatibleUpdate(void); // sample implementation void YAHM_SetRuntimeSettings(YAHM_runtimeSettings *pSettings) { FtrSet(MY_CRID, YAHM_FTR_ID, (UInt32)pSettings); } YAHM_runtimeSettings *YAHM_GetRuntimeSettingsPtr(void) { YAHM_runtimeSettings *pSet = NULL; FtrGet(CRID, YAHM_FTR_ID, (UInt32 *)&pSet); if (pSet ===== NULL){ pSet = MemPtrNew(sizeof(YAHM_runtimeSettings)); YAHM_SetRuntimeSettings(pSet); MemPtrSetOwner(pSet, 0); MemSet(pSet, sizeof(YAHM_runtimeSettings), 0); } return pSet; }
Developer is responsible for allocating and saving YAHM library runtime settings. YAHM_GetRuntimeSettingsPtr function should return pointer to persistent structure. For the first time this function should allocate memory chunk for settings. The best way is saving pointer to structure in feature memory. Runtime settings should be valid from the first hack activation up to device reset. You can share one runtime settings between different programs.
Function YAHM_warnAboutIncompatibleUpdate is called when several programs (or two different program versions) are used different versions of YAHM Library. This function usually should call SysFatalAlert.
The meaning of thunk types
Thunk is a piece of code and data that sits between syscall table and your patch code. Thunk was introduced to solve two problems: to prevent patch chain breaks and to execute runtime startup.
Different thunk types require different code configurations. Currently YAHMLib supports three type of thunks.
Thunks for GCC
- THUNK_COMMON. It's the easiest one. This thunk saves registers on own stack and completely transparent for GCC compiler. This is the slowest kind of thunk. It can be used as most common thunk type. This thunk support .got relocation
- THUNK_FAST. The fastest thunk. GCC users can use it for time critical syscalls. This thunk don't support .got relocation.
- THUNK_CW. Thunk for CodeWarrior compiler. Can be used with GCC. Saves two registers on stack and cause “5th parameter shift”. Search for details in CodeWarrior hack samples. This thunk support .got relocation
Thunks for CodeWarrior
- THUNK_COMMON. It's the easiest one. This thunk saves registers on different stack. This thunk require ARMlet_Startup function to be called first for CodeWarior users. Patch function have name ARMlet_Main. This is the slowest kind of thunk.
- THUNK_FAST. The fastest thunk. CodeWarrior users can't use it.
- THUNK_CW. This thunk save registers and prepare R10 like ARMlet_Startup. You can use this thunk instead of ARMlet_Startup. Just set this type of stack in TRA5 resource and remove ARMlet_Startup.c from project. Don't forget to rename patch function to ARMlet_Startup