在 gcc 中使用内联汇编程序调用方法
Posted
技术标签:
【中文标题】在 gcc 中使用内联汇编程序调用方法【英文标题】:Calling method using inline assembler in gcc 【发布时间】:2012-10-04 17:22:35 【问题描述】:正如我所说,我正在尝试使用 gcc 调用使用内联 asm 的方法。所以,我搜索了 x86 的工作原理,以及调用约定是什么,然后我尝试了一些简单的调用女巫,效果很好。然后我尝试嵌入v8,这是我最初的目标,但它并没有那么好...... 这是我的代码:
v8::Handle<v8::Value> V8Method::staticInternalMethodCaller(const v8::Arguments& args, int argsize, void* object, void* method)
int i = 0;
char* native_args;
// Move the ESP to the end of the array (argsize is the array size in byte)
asm("subl %1, %%esp;"
"movl %%esp, %0;"
: "=r"(native_args)
: "r"(argsize));
// This for loop only converts V8 type to native type,
// and puts them in the array:
for (; i < args.Length(); ++i)
if (args[i]->IsInt32())
*(int*)(native_args) = args[i]->Int32Value();
native_args += sizeof(int);
else if (args[i]->IsNumber())
*(float*)(native_args) = (float)(args[i]->NumberValue());
native_args += sizeof(float);
// Then call the method:
asm("call *%1;" : : "c"(object), "r"(method));
return v8::Null();
这是生成的程序集:
__ZN3srl8V8Method26staticInternalMethodCallerERKN2v89ArgumentsEiPvS5_:
LFB1178:
.cfi_startproc
.cfi_personality 0,___gxx_personality_v0
.cfi_lsda 0,LLSDA1178
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
subl $68, %esp
.cfi_offset 3, -12
movl $0, -12(%ebp)
movl 12(%ebp), %eax
/APP
# 64 "method.cpp" 1
subl %eax, %esp; movl %esp, %ebx; addl $4, %esp
# 0 "" 2
/NO_APP
movl %ebx, -16(%ebp)
jmp L74
L77:
movl -12(%ebp), %eax
movl %eax, (%esp)
movl 8(%ebp), %ecx
LEHB25:
call __ZNK2v89ArgumentsixEi
LEHE25:
subl $4, %esp
movl %eax, -36(%ebp)
leal -36(%ebp), %eax
movl %eax, %ecx
call __ZNK2v86HandleINS_5ValueEEptEv
movl %eax, %ecx
LEHB26:
call __ZNK2v85Value7IsInt32Ev
LEHE26:
testb %al, %al
je L75
movl -12(%ebp), %eax
movl %eax, (%esp)
movl 8(%ebp), %ecx
LEHB27:
call __ZNK2v89ArgumentsixEi
LEHE27:
subl $4, %esp
movl %eax, -32(%ebp)
leal -32(%ebp), %eax
movl %eax, %ecx
call __ZNK2v86HandleINS_5ValueEEptEv
movl %eax, %ecx
LEHB28:
call __ZNK2v85Value10Int32ValueEv
LEHE28:
movl %eax, %edx
movl -16(%ebp), %eax
movl %edx, (%eax)
movl -16(%ebp), %eax
movl (%eax), %ebx
movl $LC4, 4(%esp)
movl $__ZSt4cout, (%esp)
LEHB29:
call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl -16(%ebp), %edx
movl %edx, (%esp)
movl %eax, %ecx
call __ZNSolsEPKv
subl $4, %esp
movl $LC5, 4(%esp)
movl %eax, (%esp)
call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl %ebx, (%esp)
movl %eax, %ecx
call __ZNSolsEi
subl $4, %esp
movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)
movl %eax, %ecx
call __ZNSolsEPFRSoS_E
subl $4, %esp
addl $4, -16(%ebp)
jmp L76
L75:
movl -12(%ebp), %eax
movl %eax, (%esp)
movl 8(%ebp), %ecx
call __ZNK2v89ArgumentsixEi
LEHE29:
subl $4, %esp
movl %eax, -28(%ebp)
leal -28(%ebp), %eax
movl %eax, %ecx
call __ZNK2v86HandleINS_5ValueEEptEv
movl %eax, %ecx
LEHB30:
call __ZNK2v85Value8IsNumberEv
LEHE30:
testb %al, %al
je L76
movl -12(%ebp), %eax
movl %eax, (%esp)
movl 8(%ebp), %ecx
LEHB31:
call __ZNK2v89ArgumentsixEi
LEHE31:
subl $4, %esp
movl %eax, -24(%ebp)
leal -24(%ebp), %eax
movl %eax, %ecx
call __ZNK2v86HandleINS_5ValueEEptEv
movl %eax, %ecx
LEHB32:
call __ZNK2v85Value11NumberValueEv
LEHE32:
fstps -44(%ebp)
flds -44(%ebp)
movl -16(%ebp), %eax
fstps (%eax)
movl -16(%ebp), %eax
movl (%eax), %ebx
movl $LC4, 4(%esp)
movl $__ZSt4cout, (%esp)
LEHB33:
call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl -16(%ebp), %edx
movl %edx, (%esp)
movl %eax, %ecx
call __ZNSolsEPKv
subl $4, %esp
movl $LC5, 4(%esp)
movl %eax, (%esp)
call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl %ebx, (%esp)
movl %eax, %ecx
call __ZNSolsEf
subl $4, %esp
movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)
movl %eax, %ecx
call __ZNSolsEPFRSoS_E
subl $4, %esp
addl $4, -16(%ebp)
L76:
incl -12(%ebp)
L74:
movl 8(%ebp), %ecx
call __ZNK2v89Arguments6LengthEv
cmpl -12(%ebp), %eax
setg %al
testb %al, %al
jne L77
movl 16(%ebp), %eax
movl 20(%ebp), %edx
movl %eax, %ecx
/APP
# 69 "method.cpp" 1
call *%edx;
# 0 "" 2
/NO_APP
call __ZN2v84NullEv
leal -20(%ebp), %edx
movl %eax, (%esp)
movl %edx, %ecx
call __ZN2v86HandleINS_5ValueEEC1INS_9PrimitiveEEENS0_IT_EE
subl $4, %esp
movl -20(%ebp), %eax
jmp L87
L83:
movl %eax, (%esp)
call __Unwind_Resume
L84:
movl %eax, (%esp)
call __Unwind_Resume
L85:
movl %eax, (%esp)
call __Unwind_Resume
L86:
movl %eax, (%esp)
call __Unwind_Resume
LEHE33:
L87:
movl -4(%ebp), %ebx
leave
.cfi_restore 5
.cfi_restore 3
.cfi_def_cfa 4, 4
ret
.cfi_endproc
所以,这个静态方法是一个回调(我之前做了一些签名检查),女巫应该调用提供有效 C++ 本机参数的特定方法。为了加快一点速度并避免 args 的副本,我尝试将所有参数加载到本地数组中,然后修改 ESP 以使该数组成为参数。
方法调用运行良好,但我没有得到正确的参数...我已经对函数调用、调用约定和大量测试(都成功)进行了大量研究,但我没有了解发生了什么...我错过了什么吗?
基本上,被调用者应该在 esp 的顶部获取它的参数,在我的例子中,是数组...(我准确地说数组是有效的)
我使用 GCC。
【问题讨论】:
不要为自己是法国人或不是专业人士而感到抱歉,但请做为给我们造成这样的混乱感到非常抱歉!请格式化您的代码,以便普通人可以阅读它...(这涉及新行、空格、缩进、typedef 等)谢谢! 【参考方案1】:你正在尝试的有很多问题。
您不能使用内联汇编修改%esp
,因为编译器
可能是使用%esp
来引用它的局部变量和参数。如果编译器改用%ebp
,这可能有效,但不能保证。
在返回之前,您永远不会撤消 %esp
修改。
在你的内联汇编中,你需要声明%esp
是副作用的。
您可能需要将object
作为静默的第一个参数传递。 method
是实例方法,不是静态方法?
所有这些都取决于您使用的调用约定:cdecl
、stdcall
等。
【讨论】:
编译器使用%ebp
,我知道没有保证,但是当我查看生成的ASM时,它使用%ebp
。是的,我忘记撤消 %esp
修改,但这并没有改变 args 在被调用者内部无效的事实......副作用?在 c++ 中,只有使用 %ecx
传递对象 ("c"(object)
) 的 THISCALL 约定,其作用类似于 cdecl 的参数...
更多可能性:像args[i]->Int32Value()
这样的调用正在破坏您的本机参数数组?也许float
应该作为double
传递?错误的论点究竟是什么样的?
顺便说一句,gcc 上的thiscall
不会通过%ecx
中的this
。它在堆栈上传递。 en.wikipedia.org/wiki/X86_calling_conventions#thiscall
我不这么认为,因为通过在 %esp
中添加或替换 4 或 8 之类的值,args 有效(但如果我更改 args,例如给出 double 和 int,我必须更改该值,并且仍然是随机值),并且数组已正确填充。错误的参数看起来,嗯,像随机参数,随机整数,或随机双精度数,或 0...在%ecx
中,调用是有效的(对象和方法指针),只有参数是错误的。
我为那个函数添加了生成的程序集,但是它很长而且很难调试...【参考方案2】:
我建议不要自己尝试这样做,有很多烦人的小细节必须完全正确。我建议改为使用FFCALL library,特别是avcall 方法集来执行此操作。
我想这样的事情会做你想做的事:
v8::Handle<v8::Value> V8Method::staticInternalMethodCaller(const v8::Arguments& args, int argsize, void* object, void* method)
// Set up the argument list with the function pointer, return type, and
// pointer to value storing the return value (assuming int, change if
// necessary)
int return_value;
av_alist alist;
av_start_int(alist, method, &return_value);
for(int i = args.Length() - 1; i >= 0; i--)
// Push the arguments onto the argument list
if (args[i]->IsInt32())
av_int(alist, args[i]->Int32Value());
else if (args[i]->IsNumber())
av_double(alist, (float)(args[i]->NumberValue());
av_call(alist); // Call the function
return v8::Null();
【讨论】:
嗯,这个库看起来不错,但是我做的是C++而不是C...另外,即使我决定使用一个库,我仍然想学习,并且知道在哪里我的错误,我不明白我的代码有什么问题。以上是关于在 gcc 中使用内联汇编程序调用方法的主要内容,如果未能解决你的问题,请参考以下文章