Сборка x86 конвертируется в вызов функции ARM с различным количеством параметров в Arm - PullRequest
0 голосов
/ 25 апреля 2018

Я унаследовал сборочный фрагмент MSVC x86, который вызывает функцию класса C ++, передавая различное количество параметров, от 0 до 16 параметров. Эти параметры гарантированно будут int, float или char *. Аналогично для возвращения, это всегда один из этих трех типов.

Это для общей библиотеки Android NDK, ориентированной на Android API 19 или выше. Я пытаюсь добиться максимальной совместимости в этом отношении.

В настоящее время у меня есть этот код для x86, который я перефразировал:

void * Extension;   // Class to call on (of type Extension *)
void * Function;    // Class function to invoke on (&Extension::XX)
int ParameterCount; // from 0 through 16
int * Parameters;   // Pre-initialised to alloca() array, with parameters already set pre-ASM block
int Result = 0;     // Output here
__asm
{
    pushad                  ; Start new register set (do not interfere with already existing registers)
    mov ecx, ParameterCount ; Store ParameterCount in ecx
    cmp ecx, 0              ; If no parameters, call function immediately
    je CallNow

    mov edx, Parameters     ; Otherwise store Parameters in edx
    mov ebx, ecx            ; Copy ecx, or ParameterCount, to ebx
    shl ebx, 2              ; Multiply parameter count by 2^2 (size of 32-bit variable)
    add edx, ebx            ; add (ParameterCount * 4) to Parameters, making edx point to Parameters[param count]
    sub edx, 4              ; subtract 4 from edx, making it 0-based (ending array index)
    PushLoop:
        push [edx]          ; Push value pointed to by Parameters[edx]
        sub edx, 4          ; Decrement next loop`s Parameter index:    for (><; ><; edx -= 4)
        dec ecx             ; Decrement loop index:                     for (><; ><; ecx--)
        cmp ecx, 0          ; If ecx == 0, end loop:                    for (><; ecx == 0; ><)
        jne PushLoop        ; Optimisation: "cmp ecx, 0 / jne" can be replaced with "jcxz"
    CallNow:
    mov ecx, Extension      ; Move Extension to ecx
    call Function           ; Call the function inside Extension
    mov Result, eax         ; Function`s return is stored in eax; copy it to Result
    popad                   ; End new register set (restore registers that existed before popad)
}

Хотя я понимаю x86, я сейчас портирую его на Android NDK. Это означает, что armeabi, armeabi-v7a и попытка использовать __asm__ от Clang вместо __asm ​​в Visual Studio. Честно говоря, понятия не имею, с чего начать.

__asm__ volatile("pushad            \t\n\
    mov %%ecx, %[ParameterCount]    \t\n\
    cmp %%ecx, $0                   \t\n\
    je CallNow                      \t\n\
    mov %%edx, %[Parameters]        \t\n\
    mov %%ebx, %%ecx                \t\n\
    shl %%ebx, $2                   \t\n\
    add %%edx, %%ebx                \t\n\
    sub %%edx, $4                   \t\n\
    PushLoop:                       \t\n\
        push[%%edx]                 \t\n\
        sub %%edx, $4               \t\n\
        dec %%ecx                   \t\n\
        cmp %%ecx, $0               \t\n\
        jne PushLoop                \t\n\
    CallNow:                        \t\n\
        mov %%ecx, %[Extension]     \t\n\
        call %[Function]            \t\n\
        mov %[Result], %%eax        \t\n\
        popad"
    // outputs, memory?
    : [Result] "=m" (Result)
    // inputs, "r" indicates read, [x] indicates the ASM will reference it by %[x]
    : [Extension] "r" (Extension), [Parameters] "r" (Parameters), [Function] "r" (Function), [ParameterCount] "r" (ParameterCount));

Я получаю неожиданные токены и регистрирую проблемы повсюду. Я просмотрел несколько статей, но согласно этой статье , вызовы функций различаются для разных устройств - и они различаются в зависимости от количества параметров, что является проблемой.

NDK DLL может вызываться часто, и все коммуникации в конечном итоге проходят через этот ASM. Так что это вещь "сделай или сломай".

1 Ответ

0 голосов
/ 25 апреля 2018

Я решил похожую проблему несколько лет назад.Но моей задачей было просто вызвать функции C, а не методы C ++.Но это не должно быть проблемой для обновления моего кода.

Итак, мой код здесь.Пожалуйста, не стесняйтесь изменять и использовать его.

const int ARGC = 32;
const int ARGC_BOUNDS = 4;

HRESULT er = S_OK;

int argv[ARGC] = {0};       
int arg_pointer = 0;
int arg_stack_count = 0;

//////
//
// ..... fill argv with arguments
//
//////

// how many arguments will be placed on stack
if (arg_pointer > 4) {
    arg_stack_count = arg_pointer - 4;
}

// build stack, fill registers and call functions  
// ! volatile ... otherwise compiler "optimize out" our ASM code
__asm__ volatile (
    "mov r4, %[ARGV]\n\t"   // remember pointers (SP will be changed)
    "ldr r5, %[ACT]\n\t"    
    "ldr r0, %[CNT]\n\t"    // arg_stack_count  => R0
    "lsl r0, r0, #2\n\t"    // R0 * 4           => R0
    "mov r6, r0\n\t"        // R4               => R6           
    "mov r1, r0\n"          // arg_stack_count  => R1           
"loop: \n\t"
    "cmp r1, #0\n\t"
    "beq end\n\t"           // R1 == 0      => jump to end
    "sub r1, r1, #4\n\t"    // R1--
    "mov r3, r4\n\t"        // argv_stack   => R3
    "add r3, r3, #16\n\t"
    "ldr r2, [r3, r1]\n\t"  // argv[r1]
    "push {r2}\n\t"         // argv[r1] => push to stack
    "b loop\n"              //          => repeat
"end:\n\t"
    "ldr r0, [r4]\n\t"      // 1st argument
    "ldr r1, [r4, #4]\n\t"  // 2nd argument
    "ldr r2, [r4, #8]\n\t"  // 3rd argument
    "ldr r3, [r4, #12]\n\t" // 4th argument
    "blx r5\n\t"            // call function
    "add sp, sp, r6\n\t"    // fix stack position
    "mov %[ER], r0\n\t"     // store result
: [ER] "=r"(er)
: [ARGV] "r" (argv),
  [ACT] "m"(Action),
  [CNT] "m" (arg_stack_count)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6");

return er;

Имейте в виду, что Google потребует, чтобы все приложения Android были 64-разрядными, начиная с августа 2019 года. Поэтому было бы разумнее переписать ваше приложение, потому что водин год вам придется переписать его снова.

...