内联汇编 – cdecl和准备堆栈

我最近一直试图通过使用缓冲区和不同assembly操作符的RAWhex等价物来实现C ++中的dynamic函数。 为了说明一个简单的跳跃:

byte * buffer = new buffer[5]; *buffer = '0xE9'; // Hex for jump *(uint*)(buffer + 1) = 'address destination'; 

我在assembly方面没有经验,但是我知道足够创造非常简单的function。 现在我正在原始内存中创buildcdecl函数。 问题是,我不知道有多less我想推栈(内存)与sub 。 我们以这个函数为例:

 int MyTest(int x, int y) { return x + y; } long TheTest(int x, int y) { return MyTest(x, 5); } 08048a20 <_Z6TheTestii>: _Z6TheTestii(): 8048a20: 55 push %ebp 8048a21: 89 e5 mov %esp,%ebp 8048a23: 83 ec 18 sub $0x18,%esp 8048a26: c7 44 24 04 05 00 00 movl $0x5,0x4(%esp) 8048a2d: 00 8048a2e: 8b 45 08 mov 0x8(%ebp),%eax 8048a31: 89 04 24 mov %eax,(%esp) 8048a34: e8 c2 ff ff ff call 80489fb <_Z6MyTestii> 8048a39: c9 leave 8048a3a: c3 ret 

如您所见,首先是C ++代码,下面是“TheTest”函数的ASM。 我们可以立即注意到堆栈被压入24(0x18)字节(正如前面提到的,我没有经历使用程序集,所以我可能不会使用正确的术语和/或完全正确)。 这对我来说没有任何意义。 当只有2个不同的整数被使用时,24字节是多less? 使用了variables“x”,它是4个字节,值“5”也使用了4个字节(记住它是cdecl,所以调用函数负责关于函数参数的内存)并不能满足24 … 。

现在这是一个额外的例子,让我真的想知道程序集输出:

 int NewTest(int x, char val) { return x + val; } long TheTest(int x, int y) { return NewTest(x, (char)6); } 08048a3d <_Z6TheTestiiii>: _Z6TheTestiiii(): 8048a3d: 55 push %ebp 8048a3e: 89 e5 mov %esp,%ebp 8048a40: 83 ec 08 sub $0x8,%esp 8048a43: c7 44 24 04 06 00 00 movl $0x6,0x4(%esp) 8048a4a: 00 8048a4b: 8b 45 08 mov 0x8(%ebp),%eax 8048a4e: 89 04 24 mov %eax,(%esp) 8048a51: e8 ca ff ff ff call 8048a20 <_Z7NewTestic> 8048a56: c9 leave 8048a57: c3 ret 

这里唯一的区别(除了值)是我使用'char'(1字节)而不是一个整数的事实。 如果我们再看看汇编代码,那么只会将堆栈指针压入8个字节。 这与前面的例子相差16个字节。 作为一个彻头彻尾的C ++人,我不知道发生了什么事情。 我真的很感激,如果有人可以启发我的话题!

注:我在这里发布而不是阅读ASM书的原因是因为我需要使用汇编这一个function。 所以我不想读一整本40行的代码

编辑:我也不关心平台依赖,我关心Linux 32位🙂

TheTest创建的堆栈框架TheTest包含本地(自动)变量和由TheTest调用的函数的参数,如MyTestTheTestTheTest是由TheTest推送和弹出的,只要它足够大,可以将参数保存到它所调用的函数中,大小并不重要。

你看到的编译器输出是编译器几次传递的结果。 每次通过都可以执行转换和优化,从而减少所需的帧大小; 我怀疑在一些早期的状态编译器需要24个字节的帧,并且从来没有减少它,即使代码被优化。

编译器在您的平台上的ABI将建立您必须遵循的关于堆栈对齐的一些规则,因此要调整帧大小以满足这些要求。

这些函数使用帧指针%ebp%虽然这不是代码大小或性能的胜利; 这可能有助于调试,但。

它看起来像你的编译器是第一个功能(可能缺少堆栈使用优化)的一个错误。 编译器使用两条指令(移动到一个预先分配的堆栈槽)而不是单个的按下指令也是很奇怪的。

你没有优化编译? 你可以发布你的编译器命令行吗?

这是为了使堆栈对齐到32个字节的倍数,以便SIMD指令可以与堆栈中的变量一起使用。

有一些序言和结尾代码被插入到这些函数中。 尝试在裸函数中编写程序集,即

 __declspec( naked ) void UsernameIdTramp() // 10 byter, 5 bytes saves + 5 bytes for tramp { __asm { nop; nop; nop; nop; nop; // 5 bytes copied from target - nop; nop; nop; nop; nop; // 5 bytes for the jump back. } }