汇编 x86 转换为 ARM 函数调用,具有不同数量的 Arm 参数
Assembly x86 convert to ARM function call with varying number of parameters to Arm
我继承了一个 x86 MSVC 程序集,它调用 C++ class 函数,传递不同数量的参数,从 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,并尝试使用 Clang 的 __asm__ 而不是 Visual Studio 的 __asm。坦率地说,我不知道从哪里开始。
__asm__ volatile("pushad \t\n\
mov %%ecx, %[ParameterCount] \t\n\
cmp %%ecx, [=11=] \t\n\
je CallNow \t\n\
mov %%edx, %[Parameters] \t\n\
mov %%ebx, %%ecx \t\n\
shl %%ebx, \t\n\
add %%edx, %%ebx \t\n\
sub %%edx, \t\n\
PushLoop: \t\n\
push[%%edx] \t\n\
sub %%edx, \t\n\
dec %%ecx \t\n\
cmp %%ecx, [=11=] \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));
我收到了意想不到的令牌,并且到处都是注册问题。我查阅了一些文章,但根据 this article,函数调用因设备而异 - 而且它们因参数数量而异,这两者都是问题。
NDK DLL 可能会被经常调用,所有通信最终都会通过该 ASM。所以这是一个成败攸关的事情。
几年前我解决过类似的问题。但我的任务只是调用 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;
请记住,从 2019 年 8 月开始,Google 将要求所有 Android 应用程序为 64 位。因此,重写您的应用程序会更明智,因为在一年内您将拥有重写一遍。
我继承了一个 x86 MSVC 程序集,它调用 C++ class 函数,传递不同数量的参数,从 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,并尝试使用 Clang 的 __asm__ 而不是 Visual Studio 的 __asm。坦率地说,我不知道从哪里开始。
__asm__ volatile("pushad \t\n\
mov %%ecx, %[ParameterCount] \t\n\
cmp %%ecx, [=11=] \t\n\
je CallNow \t\n\
mov %%edx, %[Parameters] \t\n\
mov %%ebx, %%ecx \t\n\
shl %%ebx, \t\n\
add %%edx, %%ebx \t\n\
sub %%edx, \t\n\
PushLoop: \t\n\
push[%%edx] \t\n\
sub %%edx, \t\n\
dec %%ecx \t\n\
cmp %%ecx, [=11=] \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));
我收到了意想不到的令牌,并且到处都是注册问题。我查阅了一些文章,但根据 this article,函数调用因设备而异 - 而且它们因参数数量而异,这两者都是问题。
NDK DLL 可能会被经常调用,所有通信最终都会通过该 ASM。所以这是一个成败攸关的事情。
几年前我解决过类似的问题。但我的任务只是调用 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;
请记住,从 2019 年 8 月开始,Google 将要求所有 Android 应用程序为 64 位。因此,重写您的应用程序会更明智,因为在一年内您将拥有重写一遍。