內聯彙編 - cdecl 和準備堆棧 (Inline assembly - cdecl and preparing the stack)


問題描述

內聯彙編 ‑ cdecl 和準備堆棧 (Inline assembly ‑ cdecl and preparing the stack)

I've recently been trying to implement dynamic functions in C++ by using a buffer and RAW hexadecimal equivalents of different assembly operators. To illustrate a simple jump:

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

I am not experienced in assembly but I know enough to create very simple functions. Right now I'm creating cdecl functions in raw memory. The problem is, I do not know how much I want to push the stack (for memory) with sub. Let's take this function as an example:

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    

As you can see, first is the C++ code and below is the ASM of the 'TheTest' function. One can instantly notice that the stack is pushed for 24 (0x18) bytes (as previously mentioned, I am not experienced using assembly so I might not use the correct terms and/or be completely right). This does not make any sense for me. How come 24 bytes is required when only 2 different integers are used? The variable 'x' is used, which is 4 bytes, and the value '5' which also uses 4 bytes (remember it's cdecl so the calling function takes care of memory regarding the function arguments) does not make up for 24....

Now here is an additional example which makes me really wonder about the assembly output:

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    

The only difference here (except the values)  is the fact that I use a 'char' (1 byte) instead of an integer. If we then look at the assembly code, this pushes the stack pointer for only 8 bytes. That's a difference of 16 bytes from the previous example. As an out‑and‑out C++ person, have I no clue what's going on. I would really appreciate if someone could enlighten me on the subject!

NOTE: The reason why I'm posting here instead of reading an ASM book, is because I need to use assembly for this one function. So I don't want to read a whole book for 40 lines of code...

EDIT: I also do not care for platform‑dependency, I only care about Linux 32bit :)

‑‑‑‑‑

參考解法

方法 1:

The stack frame created in TheTest holds both local (automatic) variables and arguments to functions, such as MyTest and NewTest, called by TheTest. The frame is pushed and popped by TheTest, so as long as it is big enough to hold the arguments to the functions it calls, the size doesn't matter much. 

The compiler output you are seeing is the result of several passes of the compiler. Each pass may perform transformations and optimizations that reduce the frame size required; I suspect at some early state the compiler needed 24 bytes of frame, and never reduced it even though the code was optimized.

The ABI of the compiler on your platform will establish some rules about stack alignment that you must follow, so frame sizes are rounded up to meet these requirements.

These functions use the frame pointer %ebp% though this is not a win in code size or performance; this may aid debugging, though.

方法 2:

It looks to me like your compiler is making a mistake for the first function (probably missing a stack usage optimization). It's also odd that your compiler is using two instructions (with move to a pre‑allocated stack slot) rather than a single push instruction.

Are you compiling without optimization? Could you post your compiler command line?

方法 3:

This is to keep the stack aligned to multiple of 32 bytes so that SIMD instructions can be used with variables on the stack.

方法 4:

There is some prologue and epilogue code being inserted into these functions. Try writing your assembly in naked functions, i.e. 

__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.
    }
}

(by Elliott DarfinkDoug CurrieNeeraj SinghTimothy BaldwinAdam Dreaver)

參考文件

  1. Inline assembly ‑ cdecl and preparing the stack (CC BY‑SA 3.0/4.0)

#32-bit #assembly #linux #GCC #C++






相關問題

內聯彙編 - cdecl 和準備堆棧 (Inline assembly - cdecl and preparing the stack)

來自 32 位用戶代碼的 64 位系統中的 ioctl 命令錯誤 (ioctl command wrong in 64 bit system from 32 bit user code)

Baiklah, PR: Bagaimana mungkin X[i] diinterpretasikan sama dengan i[X] di C? (Alright, homework: How can X[i] possibly be interpretted the same as i[X] in C?)

x32 ABI - гэта інструмент, як ім карыстацца (x32 ABI is this a tool ,how to use this)

Biên dịch Visual Studio 2012 32bit trên hệ thống 64bit (Visual Studio 2012 32bit compilation on 64bit system)

如何讓 Netbeans 7.2 使用 32 位 JVM (How get Netbeans 7.2 to use 32 Bit JVM)

反彙編代碼中的數組聲明 (Declaration of an array in disassembled code)

用C編寫跨平台應用程序 (Writing cross-platform apps in C)

為什麼 BinaryReader.ReadUInt32() 反轉位模式? (Why does BinaryReader.ReadUInt32() reverse the bit pattern?)

32 位 RHEL 機器上的內存使用 (Memory use on 32 bit RHEL machine)

將 32 位應用程序安裝到 C:\Program Files\ 而不是 C:\Program Files(x86)\ 會有什麼負面影響? (What would be the negative effects of installing a 32bit app into the C:\Program Files\ instead of the C:\Program Files(x86)\?)

Arduino 將浮點數轉換為十六進制 IEEE754 單精度 32 位 (Arduino convert float to hex IEEE754 Single precision 32-bit)







留言討論