ç手动调用函数栈和寄存器 [英] C manually call function with stack and register

查看:208
本文介绍了ç手动调用函数栈和寄存器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道这是什么大不了的操纵堆栈,但我认为这将是对我来说是很大的教训。
IM在网上搜索,我发现调用约定。我知道它的工作以及为什么。我whant模拟一些被调用方清理堆栈的可能STDCALL,其FASTCALL并不重要,重要的自己:那是谁清理栈,然后我就会有更少的工作做的:)

i know this is the big deal to manipulate stack but i think it would be a great lesson for me. im searched the internet, and i found calling convention. I know how its working and why. I whant to simulate some of "Callee clean-up stack" maybe stdcall, fastcall its doesnt matter, important think is that who clean-up stack, then i will be have less work do to :)

例如。
我在C函数

for example. i have function in C

double __fastcall Add(int a, int b) {
    return a + b;
}

这将是飞翔距离

和我有指针与void *类型此功能,

and i have pointer to this function with type void*,

void* p = reinterpreted_cast<void*>(Add);

和我有来电显示功能

void Call(void* p, int a, int b) {

    //some code about push and pop arg
    //some code about save p into register
    //some code about move 'p' into CPU to call this function manually
    //some code about take of from stack/maybe register the output from function
}

和多数民众赞成它,它的有用的,当我使用调用约定卡莱清理因为我不需要

And thats it, its helpful when i use calling convention "Calle clean-up" because i dont need

//some code about cleans-up this mess

我不知道该怎么做了,我知道它可以用汇编完成。但我怕这件事了,我从来没有'触摸'这门语言。我将不胜感激模拟与C调用,但任何人都可以ASM做,我会haapy:)

I dont know how to do it, i know it can be done with assembler. but i afraid about it, and i never 'touch' this language. i would be greatful to simulate that calling with C, but when anyone can do it with ASM i will be haapy :)

我还告诉了我whant用它做,
当我将知道如何手动调用的功能,我将能够调用函数与几个参数(如果我知道的数量和它的大小)和任何类型的功能。
所以我就可以调用任何函数中的任何语言​​时,该功能是在正确调用约定。

I told also what i whant to do with it, when i will be know how to manually call function, i will be able to call function with several parameters(if i know the number and size of it) and any type of function. so i will be able to call any function in any language when that function is in the right calling convention.

我使用的是Windows操作系统的x64和MinGW

I'm using Windows OS x64 and MinGw

推荐答案

首先:C被有意隐藏调用约定和特定于如何你code从编程执行的一切,并提供了一​​个抽象层,它上面。

First of all: C is intended to hide calling conventions and everything that is specific to how your code is executed from the programmer and provide an abstract layer above it.

当你需要(像你说的)手动调用一个函数,唯一的条件是,当你做到这一点从 ASM

The only condition when you need to (as you say) "manually" call a function is when you do it from asm.

C作为一种语言在堆栈或程序计数器不能直接控制。

C as a language has no direct control over the stack or the program counter.

要从GCC手册列举了 FASTCALL 用于x86:

To cite from GCC manual for fastcall for x86:

 On the Intel 386, the `fastcall' attribute causes the compiler to
 pass the first two arguments in the registers ECX and EDX.
 Subsequent arguments are passed on the stack. The called function
 will pop the arguments off the stack. If the number of arguments
 is variable all arguments are pushed on the stack.

此外,据我记得返回值在 EAX 过去了。

因此​​,为了调用这个方法,你需要提供 ECX EDX 参数的函数,然后调用呼叫在函数地址指令

So in order to call a function in this way you need to provide the arguments in ECX, EDX and then invoke the call instruction on the function address

int __fastcall Add(int a, int b) {
    return a + b;
}

请注意我已经改变了返回类型为 INT ,因为我不记得双打是如何传回做。

Please note I have changed the return type to int, because I do not remember how doubles are passed back.

int a, b;
// set a,b to something

void* p = reinterpreted_cast<void*>(Add);
int return_val;
asm (
    "call %3"
    : "=a" (return_val) // return value is passed in eax
    : "c" (a) // pass c in ecx
    , "d" (b) // pass b in edx
    , "r" (p) // pass p in a random free register
);

通过调用约定它是由被叫方清理任何使用堆栈空间。在这种情况下,我们没有使用任何的,但如果我们没有那么你的编译器将会把你的函数,它会自动清理堆栈的方式添加。

在code以上实际上是在我使用GCC扩展 ASM 语法来自动把我们的变量纳入相应的寄存器这样一劈。它会生成解决这个足以code ASM 调用,以确保数据的一致性。

The code above is actually a hack in such a way that I use the GCC extended asm syntax to automatically put our variables into the appropriate registers. It will generate sufficient code around this asm call to make sure data is consistent.

如果你想使用一个基于堆栈调用约定,那么 CDECL 是标准的一

If you wish to use a stack based calling convention then cdecl is the standard one

int __cdecl Add(int a, int b) {
    return a + b;
}

然后,我们需要的参数压栈调用之前

Then we need to push the arguments to the stack prior to calling

asm (
    "push %1\n" // push a to the stack
    "push %2\n" // push b to the stack
    "call %3"   // the callee will pop them from the stack and clean up
    : "=a" (return_val) // return value is passed in eax
    : "r" (a) // pass c in any register
    , "r" (b) // pass b in any register
    , "r" (p) // pass p in any register
);

这是我没有提到的一件事是,这个 ASM 调用不会保存任何我们在使用的寄存器,所以我不建议把这个在一个函数做别的。在32位x86有 PUSHAD 的指令,将推动所有的通用寄存器堆栈具有同等的( POPAD )恢复。对于x86_64的一个等价不可用,但。通常,当您编译C code编译器会知道哪些寄存器都在使用,将它们保存在为了使被叫方没有覆盖它们。这里没有。如果被叫方使用正在使用的寄存器由调用者 - 它们将被覆盖。

One thing that I have not mentioned is that this asm call does not save any of our in-use registers, so I do not recommend putting this in a function that does anything else. In 32 bit x86 there is an instruction pushad that will push all general purpose registers to the stack and an equivalent (popad) to restore them. An equivalent for x86_64 is unavailable though. Normally when you compile C code the compiler will know which registers are in use and will save them in order for the callee not to overwrite them. Here it does not. If your callee uses registers that are in use by the caller - they will be overwritten!

这篇关于ç手动调用函数栈和寄存器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆