8, 16, 32 bits integers are always passed, on the stack, as full width 32 bits values1.
No extension, signed or zeroed, is needed.
The callee will just use the lower part of the full width values.
//C prototype of the callee void __attribute__((cdecl)) foo(char a, short b, int c, long d); foo(-1, 2, -3, 4); ;Call to foo in assembly push DWORD 4 ;d, long is 32 bits, nothing special here push DWORD 0fffffffdh ;c, int is 32 bits, nothing special here push DWORD 0badb0002h ;b, short is 16 bits, higher WORD can be any value push DWORD 0badbadffh ;a, char is 8 bits, higher three bytes can be any value call foo add esp, 10h ;Clean up the stack 64 bits values are passed on the stack using two pushes, respecting the littel endian convention2, pushing first the higher 32 bits then the lower ones.
//C prototype of the callee void __attribute__((cdecl)) foo(char a, short b, int c, long d); foo(0x0123456789abcdefLL); ;Call to foo in assembly push DWORD 89abcdefh ;Higher DWORD of 0123456789abcdef push DWORD 01234567h ;Lower DWORD of 0123456789abcdef call foo add esp, 08h 8 bits integers are returned in AL, eventually clobbering the whole eax.
16 bits integers are returned in AX, eventually clobbering the whole eax.
32 bits integers are returned in EAX.
64 bits integers are returned in EDX:EAX, where EAX holds the lower 32 bits and EDX the upper ones.
//C char foo() { return -1; } ;Assembly mov al, 0ffh ret //C unsigned short foo() { return 2; } ;Assembly mov ax, 2 ret //C int foo() { return -3; } ;Assembly mov eax, 0fffffffdh ret //C int foo() { return 4; } ;Assembly xor edx, edx ;EDX = 0 mov eax, 4 ;EAX = 4 ret 1 This keep the stack aligned on 4 bytes, the natural word size. Also an x86 CPU can only push 2 or 4 bytes when not in long mode.
2 Lower DWORD at lower address