// структура и прототип взяты из sdk от mobile-stream
typedef struct {
UInt32 dbType;
UInt32 dbCreator;
UInt32 revision;
UInt32 entries;
UInt32 rsrcTypeData0;
UInt32 rsrcTypeCode0;
UInt32 rsrcTypeCode1;
UInt16 rsrcIDData0;
UInt16 rsrcIDCode0;
UInt16 rsrcIDCode1;
UInt16 reserved;
} SysModuleDescriptorType;
void SysLinkerStub(const SysModuleDescriptorType *moduleP, UInt32 clientID, void **dispatchTablePP);
/*
* Вызов процедуры при неявном связывании происходит в три этапа. Инфраструктуру
* строит специальный препроцессор, используя библиотеку экспорта библиотеки.
* Каждый этап является законченным фрагментом кода. Первый фрагмент генерируется
* для каждой функции, второй - для каждой билиотеки, а третий - один на все вызовы.
*
* Неявное связывание происходит при первом вызове функции из модуля. Этот вызов загружает
* библиотеку и записывает в определенное место в памяти адрес таблицы экспорта.
* Все последующие вызовы вызывают функции напрямую.
*
* Список модифицируемых команд в функциях
* 1. В ExportedFunction1
* 1.1 LDR R12, [R12,#CURRENT_MODULE_INDEX]
* вписывается индекс текущего модуля
* 1.2 LDR R12, [R12,#LIB1_DISPATCH_ADDRESS_OFFSET_IN_GLOBALS]
* вписывается смещение глобальной переменной с адресом таблицы экспорта
* 1.3 ADDNE PC, R12, #EXPORTED_FUNCTION1_ORDINAL
* вписывается смещение функции в таблице экспорта (номер функции * 4)
* 2. В LoadLib1
* 2.1 Формируется структура lib1Desc
* 3. В функции ImplicitLoadLibrary
* 3.1 LDR R2, [R2,#CURRENT_MODULE_INDEX]
* вписывается индекс текущего модуля
*/
#define CURRENT_MODULE_INDEX 0x200
#define LIB1_DISPATCH_ADDRESS_OFFSET_IN_GLOBALS 0x10
#define EXPORTED_FUNCTION1_OFFSET (20 * 4)
Err ExportedFunction1(UInt32 param0, UInt32 param1, UInt32 param2, UInt32 param3, UInt32 param4, UInt32 param5, UInt32 param6){
// загрузить в R12 указатель на сегмент данных используемого модуля
_asm LDR R12, [R9]
ExportedFunction1_LabelWithCurrentModuleIndexAddr:
_asm LDR R12, [R12,#CURRENT_MODULE_INDEX]
ExportedFunction1_LabelWithDispatchAddr:
// загрузить в R12 адрес таблицы экспорта вызываемого модуля
_asm LDR R12, [R12,#LIB1_DISPATCH_ADDRESS_OFFSET_IN_GLOBALS]
// проверить, что библиотека уже загружена (адрес != 0),
_asm CMP R12, #0
// если билиотека загружена, то перейти на функцию по смещению в библиотеке
_asm ADDNE PC, R12, #EXPORTED_FUNCTION1_OFFSET
// если функция не загружена, то перейти ко второй фазе,
// предварительно сохранив в стеке адрес метки ExportedFunction1_Addr
// для повторного вызова после загрузки библиотеки
_asm STMFD SP!, {PC}
_asm B LoadLib1
ExportedFunction1_BackAddr:
}
Err LoadLib1(UInt32 param0, UInt32 param1, UInt32 param2, UInt32 param3, UInt32 param4, UInt32 *backAddr, UInt32 param5, UInt32 param6){
_asm ADR R12, lib1Desc
_asm STMFD SP!, {R12}
_asm B ImplicitLoadLibrary
}
// константный дескриптор следует в коде сразу после функции
const SysModuleDescriptorType lib1Desc = {'aexo', 'HsEx', 1, 0x6E, 'amdd', 'amdi', 'amdc', 0, 0, 0, 0};
Err ImplicitLoadLibrary(UInt32 param0, UInt32 param1, UInt32 param2, UInt32 param3, UInt32 param4,
SysModuleDescriptorType *pDescr, UInt32 *backAddr, UInt32 param5, UInt32 param6){
/* сохранить регистры.
* после операции в стеке располагаются:
* SP + 00 R0
* SP + 04 R1
* SP + 08 R2
* SP + 0C R3
* SP + 10 LR
* SP + 14 pDescr
* SP + 18 backAddr
* SP + 1C param5
* SP + 20 param6
*/
_asm STMFD SP!, {R0-R3,LR}
// R1 = ImplicitLoadLibrary_Label0 (ImplicitLoadLibrary_Label1 - 4)
_asm SUB R1, PC, #4
// R2 = ImplicitLoadLibrary_Label2
ImplicitLoadLibrary_Label0:
_asm STMFD SP!, {PC}
ImplicitLoadLibrary_Label1:
_asm LDMFD SP!, {R2}
ImplicitLoadLibrary_Label2:
// R2 = ImplicitLoadLibrary_Label2 - ImplicitLoadLibrary_Label0 + 0x14 (0x1C)
_asm SUB R2, R2, R1
_asm ADD R2, R2, #0x14
// *backAddr -= ImplicitLoadLibrary_Label2 - ImplicitLoadLibrary_Label0 + 0x14 (0x1C)
// скорректировать backAddr дабы он указывал на ExportedFunction1
_asm LDR R1, [SP,#0x18]
_asm SUB R1, R1, R2
_asm STR R1, [SP,#0x18]
// R0 = *(ExportedFunction1_LabelWithDispatchAddr)
// R0 = "LDR R12, [R12,#LIB1_DISPATCH_ADDRESS_OFFSET_IN_GLOBALS]"
_asm LDR R0, [R1,#8]
// R3 = 0xFFF
_asm MOV R3, #0x1000
_asm SUB R3, R3, #1
// R0 = LIB1_DISPATCH_ADDRESS_OFFSET_IN_GLOBALS
_asm AND R0, R0, R3
// загрузить в R12 указатель на сегмент данных используемого модуля
_asm LDR R2, [R9]
_asm LDR R2, [R2,#CURRENT_MODULE_INDEX]
// загрузить в R2 адрес таблицы экспорта вызываемого модуля
_asm ADD R2, R2, R0
// R0 = *ExportedFunction1_LabelWithCurrentModuleIndexAddr
// R0 = "LDR R12, [R12,#CURRENT_MODULE_INDEX]"
_asm LDR R0, [R1,#4]
// R1 = CURRENT_MODULE_INDEX / 4
_asm AND R1, R0, R3
_asm MOV R1, R1,LSR#2
_asm LDR R0, [SP,#0x14]
_asm BL SysLinkerStub
// восстановить регистры
_asm LDMFD SP!, {R0-R3,LR}
// выкинуть из стека pDescr
_asm LDMFD SP!, {R12}
// R12 = backAddr
_asm LDMFD SP!, {R12}
// перейти на backAddr
_asm MOV PC, R12
}