Sys Linker Stub

// структура и прототип взяты из 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
}