views:

1667

answers:

4

What is stack alignment? Why is it used? Can it be controlled by compiler settings?

The details of this question are taken from a problem faced when trying to use ffmpeg libraries with msvc, however what I'm really interested in is an explanation of what is "stack alignment".

The Details:

  • When runnig my msvc complied program which links to avcodec I get the following error: "Compiler did not align stack variables. Libavcodec has been miscompiled", followed by a crash in avcodec.dll.
  • avcodec.dll was not compiled with msvc, so I'm unable to see what is going on inside.
  • When running ffmpeg.exe and using the same avcodec.dll everything works well.
  • ffmpeg.exe was not compiled with msvc, it was complied with gcc / mingw (same as avcodec.dll)

Thanks,

Dan

+26  A: 

Alignment of variables in memory (a short history).

In the past computers had an 8 bits databus. This means, that each clock cycle 8 bits of information could be processed. Which was fine then.

Then came 16 bit computers. Due to downward compatibility and other issues, the 8 bit byte was kept and the 16 bit word was introduced. Each word was 2 bytes. And each clock cycle 16 bits of information could be processed. But this gave a small problem.

Lets look at a memory map:

+----+
|0000| 
|0001|
+----+
|0002|
|0003|
+----+
|0004|
|0005|
+----+
| .. |

At each address there is a byte. Which can be accessed individually. But words can only be fetched at even adresses. So if we read a word at 0000, we read the bytes at 0000 and 0001. But if we want to read the word at position 0001, we need two read accesses. First 0000,0001 and then 0002,0003 and we only keep 0001,0002.

Of course this took some extra time and that was not apreciated. So that's why they invented alignment. So we store word variables at word boundaries and byte variables at byte boundaries.

For example, if we have a structure with a byte field (B) and a word field (W) (and a very naive compiler, we get the following:

+----+
|0000| B
|0001| W
+----+
|0002| W
|0003|
+----+

Which is not fun. But when using word alignment we find:

+----+
|0000| B
|0001| -
+----+
|0002| W
|0003| W
+----+

Here memory is sacrificed for access speed.

You can imagine that when using double word (4 bytes) or quad word (8 bytes) this is even more important. That's why with most modern compilers you can chose which alignment you are using while compiling the program.

Gamecat
Great description of stack alignment!
Shaun Bouckaert
I am trying to learn assembly, and I have been struggling with understanding alignment. This totally answers my questions!
joek1975
Always glad to help somebody :-).
Gamecat
+5  A: 

IIRC, stack alignment is when variables are placed on the stack "aligned" to a particular number of bytes. So if you are using a 16 bit stack alignment, each variable on the stack is going to start from a byte that is a multiple of 2 bytes from the current stack pointer within a function.

This means that if you use a variable that is < 2 bytes, such as a char (1 byte), there will be 8 bits of unused "padding" between it and the next variable. This allows certain optimisations with assumptions based on variable locations.

When calling functions, one method of passing arguments to the next function is to place them on the stack (as opposed to placing them directly into registers). Whether or not alignment is being used here is important, as the calling function places the variables on the stack, to be read off by the calling function using offsets. If the calling function aligns the variables, and the called function expects them to be non-aligned, then the called function won't be able to find them.

It seems that the msvc compiled code is disagreeing about variable alignment. Try compiling with all optimisations turned off.

Shaun Bouckaert
sizeof(char) is always 1 byte, which is always at least 8 bits... not bytes. Alignment depends on compiler platform, and (x86, anyway) is generally 4byte for 32bit architectures, 8byte for 64bit archs.
snemarch
Thanks, was a brainfart indeed on the size of a byte :P. I had picked 16 bytes as an arbitrary example, but using the smaller example makes it much clearer.
Shaun Bouckaert
+7  A: 

Some CPU architectures require specific alignment of various datatypes, and will throw exceptions if you don't honor this rule. In standard mode, x86 doesn't require this for the basic data types, but can suffer performance penalties (check www.agner.org for low-level optimization tips).

However, the SSE instruction set (often used for high-performance) audio/video procesing has strict alignment requirements, and will throw exceptions if you attempt to use it on unaligned data (unless you use the, on some processors, much slower unaligned versions).

Your issue is probably that one compiler expects the caller to keep the stack aligned, while the other expects callee to align the stack when necessary.

EDIT: as for why the exception happens, a routine in the DLL probably wants to use SSE instructions on some temporary stack data, and fails because the two different compilers don't agree on calling conventions.

snemarch
+3  A: 

As far as I know, compilers don't typically align variables that are on the stack. The library may be depending on some set of compiler options that isn't supported on your compiler. The normal fix is to declare the variables that need to be aligned as static, but if you go about doing this in other people's code, you'll want to be sure that they variables in question are initialized later on in the function rather than in the declaration.

// Some compilers won't align this as it's on the stack...
int __declspec(align(32)) needsToBe32Aligned = 0;
// Change to
static int __declspec(align(32)) needsToBe32Aligned;
needsToBe32Aligned = 0;

Alternately, find a compiler switch that aligns the variables on the stack. Obviously the "__declspec" align syntax I've used here may not be what your compiler uses.

Dan Olson