Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

warning #13212: Reference to ebx in function requiring stack alignment

Tags:

c++

assembly

I am trying to compile the below code with ICC 2018:

__asm {
        mov ebx, xx              ;xx address to registers
}

where xx is of type int16. This is the first instruction inside my function.

I get the below warning with the above assembly code: warning #13212: Reference to ebx in function requiring stack alignment

Surprisingly, when I replaced ebx with eax or esi, I saw the warning go away. I am not able to understand why I am seeing the issue only with ebx, as far as I know, both ebx and eax has same architecture(32 bit registers).

Also, I didn't see the warning when I compiled the same code with ICC 2013.

Can anyone help me resolve this warning?

Thanks!

like image 737
Nayanika Ghosh Avatar asked Feb 28 '18 07:02

Nayanika Ghosh


1 Answers

The compiler on the platform of choice (ICC as it mimics MSVC's behavior) uses EBX to save the original stack pointer value if additional alignment is required. Therefore you cannot overwrite it safely. The program's behavior would become undefined. The compiler warning just tells you about that.

To help with save/restore of all registers affected by assembly blocks, an extended syntax with so called clobber lists is recommended. Your example uses MSVC-style __asm{...} syntax. In MSVC-style syntax, the compiler detects what registers you touch and saves/restores them for you. ICC also supports GCC-like notation for extended asm with clobber lists: asm("...":::). It also supports simpler GCC asm("...") without the clobber list part. See this question for more details (thanks Peter Cordes for the link and explanation).

Documentation that I found useful when I was learning to use clobber lists (I actually use it all the time because it is impossible to remember its rather human-unfriendly syntax):

  1. https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5
  2. https://software.intel.com/en-us/node/694235

The simple inline assembly blocks without clobber lists can be safely used only in the following situations:

  1. Instructions of the block do not modify registers defined in the ABI. Thus GPRs, stack counter, flags should be untouched; if there are floating-point calculations in the function, FPU/vector registers are off limits as well. Even memory writes can lead to bugs because the compiler relies on known values to reside in memory. In contrast, one can issue INT3, HLT, WRMSR etc instructions which either touch no registers or affect only system registers which the compiler do not use. However, the majority of such instructions are privileged and cannot be used in user applications. One can also read all available registers provided there are no side effects of such reads.
  2. The assembler block is the only statement in a function's body. In this case, it has to abide to calling conventions of the chosen platform: how function's arguments are passed, where its exit code should be placed etc. The block will also need to cope with compiler-generated prologue and epilogue code blocks that have their own assumptions about registers. Their code is not strictly stable, nor portable nor guaranteed to be the same with different optimization levels. With GCC on x86, I was unable to disable prologue/epilogue generation, so there is still some risk to violate compiler assumptions.
  3. You save all clobbered registers yourself and restore them afterwards. This is relatively easy because you can see your own assembler code and can tell if a register gets modified by it or not. However, make a mistake and a compiler will not be here for you to point it out. It is very nice of ICC 2018 to actually give a warning even though it could have just treated the asm block as a black box.
  4. You "stole" a register from compiler. GCC allows doing that with register asm statement (do not remember if the same trick works with other compilers). You can thus declare that a variable is bound to a certain register. Be aware that such technique reduces number of registers available to compiler for its register allocation phase, and that will degrade quality of code it generates. Ask for too many registers, and the compiler will be helpless and refuse to work. Similarly, one cannot ask for registers with a dedicated role to be taken away from a compiler, such as stack pointer or program counter.

That said, the extended asm syntax with clobber lists provides a nice alternative. It turns an asm section from a black box to something of an inline internal "function" that declares its own inputs, outputs and resources it overwrites which are shared with the outer function.

like image 120
Grigory Rechistov Avatar answered Nov 15 '22 06:11

Grigory Rechistov