Re: argc - cant access memory

Jim Blandy
2007-07-11T21:13:55Z

"Dave Korn" <dave.korn@artimi.com> writes:
> This is normal behaviour. It's nothing to do with optimisation: it's just
> that gdb breakpoints at the very start of the function epilogue, but none of
> the arguments parameters are accessible in the expected places indicated by
> the debug info until after the epilogue has created the stack frame, etc.
>
> Try this with any function you like: set a breakpoint on the very first line
> - the one with the opening '{' of the function, run until you hit the
> breakpoint. You'll see nonsense in the locals and arguments. Now use the
> 'next' function to step over the opening brace - which amounts to stepping
> over the function epilogue - and suddenly all those variables will spring into
> being with their correct values.
>
> It's a cosmetic glitch that gdb fails to say something like "not in scope"
> before the prologue has completed.

Daniel's explanation is right, but I wanted to clarify something here:

As long as GDB is properly interpreting the debugging info it has
(which in this case it is), then this isn't GDB's problem to solve;
it's GCC's. GDB can only debug the program as described by the
debugging information. The fault here is in GCC for not properly
describing the code it produced.

Especially nowadays, there is no clear boundary between the prologue
and the body code; body instructions may be mixed in with prologue
instructions. This means that it's not possible for GDB to accurately
declare variables "not in scope" until the end of the prologue.

DWARF does have the features required for the compiler to accurately
describe every variable's location at every point in the code, even in
the prologue, or at the function's entry point. And GDB understands
that info (although it can't assign to variables in some cases).

The code I see is:

(gdb) disass main
Dump of assembler code for function main:
0x08048354 <main+0>: lea 0x4(%esp),%ecx
0x08048358 <main+4>: and $0xfffffff0,%esp
0x0804835b <main+7>: pushl 0xfffffffc(%ecx)
0x0804835e <main+10>: push %ebp
0x0804835f <main+11>: mov %esp,%ebp
0x08048361 <main+13>: push %ebx
0x08048362 <main+14>: push %ecx
0x08048363 <main+15>: sub $0x10,%esp
0x08048366 <main+18>: mov %ecx,%ebx
0x08048368 <main+20>: movl $0x1,(%esp)
0x0804836f <main+27>: call 0x8048298 <sleep@plt>
0x08048374 <main+32>: mov (%ebx),%eax
0x08048376 <main+34>: add $0x10,%esp
0x08048379 <main+37>: pop %ecx
0x0804837a <main+38>: pop %ebx
0x0804837b <main+39>: pop %ebp
0x0804837c <main+40>: lea 0xfffffffc(%ecx),%esp
0x0804837f <main+43>: ret
End of assembler dump.

The 'lea' saves the frame's base address in %ecx; the 'and' aligns the
stack pointer. At that point, there's no fixed relationship between
the stack pointer and the base of the frame; it depends on how
misaligned the sp was originally. We save %ebp and set it from the
aligned stack pointer.

Here's the output from readelf -wfF, referring to %esp as 'r4',
%ecx as 'r1', and %ebp as 'r5':

The section .eh_frame contains:

00000000 ZERO terminator

The section .debug_frame contains:

00000000 00000010 ffffffff CIE "" cf=1 df=-4 ra=8
LOC CFA ra
00000000 r4+4 c-4

00000014 00000024 00000000 FDE cie=00000000 pc=08048354..08048380
LOC CFA r3 r4 r5 ra
08048354 r4+4 u u u c-4
08048358 r1+0 u r1 u c-4
0804835e r4+4 u r1 u c-4
0804835f r4+8 u r1 c-8 c-4
08048361 r5+8 u r1 c-8 c-4
08048363 r5+8 c-12 c-16 c-8 c-4

After the 'and' at 0x08048358, the CFA should always be %ecx+0, but as
you can see, the CFI emitted by GCC incorrectly reverts to offsets
from %esp and %ebp.