1

I've been looking at disassembly of x86 32-bit code generated by GCC vs Clang.

Environment is Ubuntu Server 16.04.2 32bit running in VMware.

Both are compiled with the same options. No optimizations so no omitting frame pointer or anything like that. Only difference is GCC was compiled with -mpreferred-stack-boundary=2 to have a 4 byte aligned stack and avoid the extra instructions managing stack alignment. clang doesn't offer this option.

In GCC disassembly, function local stack variables are referenced using negative offsets to EBP while function arguments placed on the stack before a call are either pushed to stack or mov'd into stack memory using ESP relative positive offsets (1st arg: ESP, 2nd arg: ESP+4, etc...).

Using clang, for the same source file, the generated assembly uses EBP relative negative offsets for both accessing function local vars AND accessing the head of the stack, head of the stack + 4, etc.. for placing call arguments on the stack. ESP is almost never used aside from the initial function prologue. And there's several function calls in the disassembly I'm looking at.

I'm not used to this notation and I find it a bit odd since the actual negative offset value from EBP to get to the head of the stack will always be changing as the stack grow / contracts. Whereas using ESP, it's more intuitive to know that prior to a CALL, value at ESP will be 1st arg, value at ESP+4 will be 2nd, etc...

Ultimately I suppose the result is the same because any local stack frame location can be indexed via EBP, including the head of the stack, provided you know at any given instruction how much stack memory has been allocated since the start of the stack frame.

i.e. ESP = EBP - (total number of bytes allocated so far in current frame)

Is there a reason for the difference? Purely a different implementation choice?

(I don't have the actual disassembly on hand right now but I will edit the question later to add it).

GCC 5.4

┌ (fcn) sym.check_authentication 89
│   sym.check_authentication (int arg_8h);
│ ; var int local_14h @ ebp-0x14
│ ; var int local_4h @ ebp-0x4
│ ; arg int arg_8h @ ebp+0x8
│ 0x080484cb      55             push ebp
│ 0x080484cc      89e5           mov ebp, esp
│ 0x080484ce      83ec14         sub esp, 0x14
│ 0x080484d1      c745fc000000.  mov dword [ebp - 4], 0
│ 0x080484d8      ff7508         push dword [ebp + 8]
│ 0x080484db      8d45ec         lea eax, [ebp - 0x14]
│ 0x080484de      50             push eax
│ 0x080484df      e89cfeffff     call sym.imp.strcpy
│ 0x080484e4      83c408         add esp, 8
│ 0x080484e7      6820860408     push str.brillig                      ; "brillig" @ 0x8048620
│ 0x080484ec      8d45ec         lea eax, [ebp - 0x14]
│ 0x080484ef      50             push eax
│ 0x080484f0      e86bfeffff     call sym.imp.strcmp
│ 0x080484f5      83c408         add esp, 8
│ 0x080484f8      85c0           test eax, eax
│ 0x080484fa      7507           jne 0x8048503
│ 0x080484fc      c745fc010000.  mov dword [ebp - 4], 1
│ 0x08048503      6828860408     push str.outgrabe                     ; "outgrabe" @ 0x8048628
│ 0x08048508      8d45ec         lea eax, [ebp - 0x14]
│ 0x0804850b      50             push eax
│ 0x0804850c      e84ffeffff     call sym.imp.strcmp
│ 0x08048511      83c408         add esp, 8
│ 0x08048514      85c0           test eax, eax
│ 0x08048516      7507           jne 0x804851f
│ 0x08048518      c745fc010000.  mov dword [ebp - 4], 1
│ 0x0804851f      8b45fc         mov eax, dword [ebp - 4]
│ 0x08048522      c9             leave
└ 0x08048523      c3             ret

clang 3.8.0

┌ (fcn) sym.check_authentication 121
│   sym.check_authentication (int arg_8h);
│ ; var int local_20h @ ebp-0x20
│ ; var int local_1ch @ ebp-0x1c
│ ; var int local_18h @ ebp-0x18
│ ; var int local_14h @ ebp-0x14
│ ; var int local_4h @ ebp-0x4
│ ; arg int arg_8h @ ebp+0x8
│ 0x080484a0      55             push ebp
│ 0x080484a1      89e5           mov ebp, esp
│ 0x080484a3      83ec28         sub esp, 0x28                         ; '('
│ 0x080484a6      8b4508         mov eax, dword [ebp + 8]              ; [0x8:4]=-1 ; 8(null)
│ 0x080484a9      8d4dec         lea ecx, [ebp - 0x14]
│ 0x080484ac      8945fc         mov dword [ebp - 4], eax
│ 0x080484af      c745e8000000.  mov dword [ebp - 0x18], 0
│ 0x080484b6      8b45fc         mov eax, dword [ebp - 4]
│ 0x080484b9      89e2           mov edx, esp
│ 0x080484bb      894204         mov dword [edx + 4], eax
│ 0x080484be      890a           mov dword [edx], ecx
│ 0x080484c0      894de4         mov dword [ebp - 0x1c], ecx
│ 0x080484c3      e898feffff     call sym.imp.strcpy
│ 0x080484c8      89e1           mov ecx, esp
│ 0x080484ca      8b55e4         mov edx, dword [ebp - 0x1c]
│ 0x080484cd      8911           mov dword [ecx], edx
│ 0x080484cf      c74104608604.  mov dword [ecx + 4], str.brillig      ; [0x8048660:4]=0x6c697262 ; "brillig" @ 0x8048660
│ 0x080484d6      8945e0         mov dword [ebp - 0x20], eax
│ 0x080484d9      e862feffff     call sym.imp.strcmp
│ 0x080484de      83f800         cmp eax, 0
│ 0x080484e1      0f8507000000   jne 0x80484ee
│ 0x080484e7      c745e8010000.  mov dword [ebp - 0x18], 1
│ 0x080484ee      8d45ec         lea eax, [ebp - 0x14]
│ 0x080484f1      89e1           mov ecx, esp
│ 0x080484f3      8901           mov dword [ecx], eax
│ 0x080484f5      c74104688604.  mov dword [ecx + 4], str.outgrabe     ; [0x8048668:4]=0x6774756f ; "outgrabe" @ 0x8048668
│ 0x080484fc      e83ffeffff     call sym.imp.strcmp
│ 0x08048501      83f800         cmp eax, 0
│ 0x08048504      0f8507000000   jne 0x8048511
│ 0x0804850a      c745e8010000.  mov dword [ebp - 0x18], 1
│ 0x08048511      8b45e8         mov eax, dword [ebp - 0x18]
│ 0x08048514      83c428         add esp, 0x28                         ; '('
│ 0x08048517      5d             pop ebp
└ 0x08048518      c3             ret
  • Reading up on all this a bit more I think clang may always be doing the equivalent of GCC's -maccumulate-outgoing-args option. Pre-allocating stack space in the function prologue for the call arguments (rather than pushing them as it goes). See stackoverflow.com/questions/4534791/…. Answer there suggests this is often faster on modern CPUs. – MTLPhil May 26 '17 at 1:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.