public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* What happens on the stack when calling a function? (gcc-3.4.3)
@ 2005-05-16 13:03 Daniel Hepper
  2005-05-16 13:23 ` Arturas Moskvinas
  0 siblings, 1 reply; 3+ messages in thread
From: Daniel Hepper @ 2005-05-16 13:03 UTC (permalink / raw)
  To: gcc-help

Hi folks!

I stumbled across some strange stuff on the stack when calling a
function with gcc-3.4.3.

I wanted to learn something about the stack and buffer overflows. So I
wrote this little piece of c code (s1.c):

#include <string.h>
void f( char *args) {
   char buf1[10];
   char buf2[4] = "ABC";
   strcpy (buf1, args);
}

int main (int argc, char *argv[]) {
   if (argc > 1) {
      printf("Input: %s\n", argv[1]);
      f(argv[1]);
   }
   return 0;
}

Compiled it and fired up gdb:

$ gcc -g -o s1 s1.c
$ gdb s1
[...]
(gdb) list
1       #include <string.h>
2       void f( char *args) {
3               char buf1[10];
4               char buf2[4] = "ABC";
5               strcpy (buf1, args);
6       }
7
8       int main (int argc, char *argv[]) {
9               if (argc > 1) {
10                      printf("Input: %s\n", argv[1]);
(gdb) break 6
Breakpoint 1 at 0x80483e4: file s1.c, line 6.
(gdb) run `python -c 'print "A"*10'`
Starting program: /mnt/data/studium/vi/rlp/bo/s1 `python -c 'print "A"*10'`
Input: AAAAAAAAAA

Breakpoint 1, f (args=0xbffff3c3 "AAAAAAAAAA") at s1.c:6
6       }
(gdb)

Note: I know that 10 A's is to long (10 A's + \0), but this is not the
problem here.
In fact, the program should throw a segmentation fault if it gets more
than 11 A's (and it does that on a debian box with gcc-2.95.4). But on
my system, it works with up to 23 A's.

AFAIK, when a funktion is called, the calling function pushes the
parameters and the adress of the next command after the function call
(Return Instruction Pointer, RIP) on the stack.
The function that is called saves the frame pointer (FP) on the stack
and then reserves space on the stack for local variables by decrementing
the stack pointer.
As the stack grows from top to bottom, at the breakpoint the stack
should look this:
RIP
FP
buf1
[..]
buf1
buf2
buf2
buf2

But in fact, it looks like this:

(gdb) info frame 0
Stack frame at 0xbffff170:
 eip = 0x80483e4 in f (s1.c:6); saved eip 0x8048430
 called by frame at 0xbffff190
 source language c.
 Arglist at 0xbffff168, args: args=0xbffff3c3 "AAAAAAAAAA"
 Locals at 0xbffff168, Previous frame's sp is 0xbffff170
 Saved registers:
  ebp at 0xbffff168, eip at 0xbffff16c
(gdb) x/12x buf2
0xbffff14c:     0x00434241      0x41414141      0x41414141      0xb7004141
0xbffff15c:     0xb7fd45e0      0x08048558      0xbffff178      0xbffff188
0xbffff16c:     0x08048430      0xbffff3c3      0xbffff3c3      0x00000000
(gdb)

As you can see buf2 is on the lowest adress, followed by buf1.
But what happend to the saved ebp (at 0xbffff168, value 0xbffff188) and
saved eip (at 0xbffff16c, value 0x8048430)?
Why are they not located at 0xbffff15c and 0xbffff160? Why is there an 8
byte "hole"? What is stored there?

Having a look at the assembler code:
(gdb) disass
Dump of assembler code for function f:
0x080483a4 <f+0>:       push   %ebp
0x080483a5 <f+1>:       mov    %esp,%ebp
0x080483a7 <f+3>:       sub    $0x38,%esp
0x080483aa <f+6>:       mov    0x8048538,%eax
0x080483af <f+11>:      mov    %eax,0xffffffe4(%ebp)
0x080483b2 <f+14>:      mov    0x8(%ebp),%eax
0x080483b5 <f+17>:      mov    %eax,0x4(%esp)
0x080483b9 <f+21>:      lea    0xffffffe8(%ebp),%eax
0x080483bc <f+24>:      mov    %eax,(%esp)
0x080483bf <f+27>:      call   0x80482dc <_init+72>
0x080483c4 <f+32>:      leave
0x080483c5 <f+33>:      ret

I've tested this on a gcc-2.95.4 on a Debian system and everything was
at the expected location in memory.
Assembler looks like this:

(gdb) disass
Dump of assembler code for function f:
0x8048430 <f>:  push   %ebp
0x8048431 <f+1>:        mov    %esp,%ebp
0x8048433 <f+3>:        sub    $0x18,%esp
0x8048436 <f+6>:        mov    0x80484f4,%eax
0x804843b <f+11>:       mov    %eax,0xfffffff0(%ebp)
0x804843e <f+14>:       add    $0xfffffff8,%esp
0x8048441 <f+17>:       mov    0x8(%ebp),%eax
0x8048444 <f+20>:       push   %eax
0x8048445 <f+21>:       lea    0xfffffff4(%ebp),%eax
0x8048448 <f+24>:       push   %eax
0x8048449 <f+25>:       call   0x8048338 <strcpy>
0x804844e <f+30>:       add    $0x10,%esp
0x8048451 <f+33>:       leave
0x8048452 <f+34>:       ret
End of assembler dump.

Line <f+3> seems to be the key: gcc-3.4.3 just "wastes" some space.
But why?
It may be an alignment thing for cache optimization, but it doesn't go
away with -O0...

Here are some specs about my system:

$ uname -a
Linux d6k 2.6.11-gentoo-r6 #8 SMP Wed May 11 02:12:58 CEST 2005 i686
Intel(R) Pentium(R) M processor 1.86GHz GenuineIntel GNU/Linux
$ gcc -v
Lese Spezifikationen von /usr/lib/gcc/i686-pc-linux-gnu/3.4.3-20050110/specs
Konfiguriert mit:
/var/tmp/portage/gcc-3.4.3.20050110-r2/work/gcc-3.4.3/configure
--enable-version-specific-runtime-libs --prefix=/usr
--bindir=/usr/i686-pc-linux-gnu/gcc-bin/3.4.3-20050110
--includedir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.3-20050110/include
--datadir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.3-20050110
--mandir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.3-20050110/man
--infodir=/usr/share/gcc-data/i686-pc-linux-gnu/3.4.3-20050110/info
--with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/3.4.3-20050110/include/g++-v3
--host=i686-pc-linux-gnu --disable-altivec --enable-nls
--without-included-gettext --with-system-zlib --disable-checking
--disable-werror --disable-libunwind-exceptions --disable-multilib
--disable-libgcj --enable-languages=c,c++,f77 --enable-shared
--enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu
Thread-Modell: posix
gcc-Version 3.4.3-20050110 (Gentoo 3.4.3.20050110-r2,
ssp-3.4.3.20050110-0, pie-8.7.7)

I've tried gcc with -no-pie and -no-stack-protector-all -march=i386 and
also gcc-3.3.5, but got the same behaviour.

I don't know if this behaviour is caused by some Gentoo-specific
configuration or by gcc >3, unfortunately, I've no access to another
system to do more testing.
I would be really thankful if someone could explain me this behaviour.

Greets,
Daniel Hepper

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: What happens on the stack when calling a function? (gcc-3.4.3)
  2005-05-16 13:03 What happens on the stack when calling a function? (gcc-3.4.3) Daniel Hepper
@ 2005-05-16 13:23 ` Arturas Moskvinas
  2005-05-17  8:18   ` Daniel Hepper
  0 siblings, 1 reply; 3+ messages in thread
From: Arturas Moskvinas @ 2005-05-16 13:23 UTC (permalink / raw)
  To: Daniel Hepper; +Cc: gcc-help

> Hi folks!
> 
> I stumbled across some strange stuff on the stack when calling a
> function with gcc-3.4.3.

There was similar discusion maybe month ago.
GCC just tries to align everything to 16 bytes boundary(this
optimization is very "cheap" by the way), but if you need exact
behavior then you should use -mpreferred-stack-boundary, more info
you'll find:
http://gcc.gnu.org/onlinedocs/gcc-3.4.3/gcc/i386-and-x86_002d64-Options.html
(-mpreferred-stack-boundary)


Arturas Moskvinas

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: What happens on the stack when calling a function? (gcc-3.4.3)
  2005-05-16 13:23 ` Arturas Moskvinas
@ 2005-05-17  8:18   ` Daniel Hepper
  0 siblings, 0 replies; 3+ messages in thread
From: Daniel Hepper @ 2005-05-17  8:18 UTC (permalink / raw)
  To: Arturas Moskvinas; +Cc: gcc-help

> There was similar discusion maybe month ago.
I just found it, sorry that I didn't dig a little bit deeper.
> GCC just tries to align everything to 16 bytes boundary(this
> optimization is very "cheap" by the way), but if you need exact
> behavior then you should use -mpreferred-stack-boundary, more info
> you'll find:
> http://gcc.gnu.org/onlinedocs/gcc-3.4.3/gcc/i386-and-x86_002d64-Options.html
> (-mpreferred-stack-boundary)
Ok, I've read the thread and the chapter in the manpage. This explains
much, thanks alot for your help.
But I've further question that were not answered.

I'm still working with the example from my first post:
(gdb) list 1,14
1       #include <string.h>
2       void f( char *args) {
3               char buf1[10];
4               char buf2[4] = "ABC";
5               strcpy (buf1, args);
6       }
7
8       int main (int argc, char *argv[]) {
9               if (argc > 1) {
10                      printf("Input: %s\n", argv[1]);
11                      f(argv[1]);
12              }
13              return 0;
14      }
(gdb) break 6
Breakpoint 3 at 0x80483e4: file s1.c, line 6.
(gdb) run `python -c 'print "A"*9'`
Starting program: /mnt/data/studium/vi/rlp/bo/s1 `python -c 'print "A"*9'`
Input: AAAAAAAAA

Breakpoint 3, f (args=0xbffff3cc "AAAAAAAAA") at s1.c:6
6       }
(gdb) x/12x buf2
0xbffff174: 0x00434241      0x41414141      0x41414141      0x08040041
0xbffff184: 0xbffff198      0xbffff198      0x0804841a      0xbffff3cc
0xbffff194: 0xbffff3cc      0xb7fd617c      0xb7eb6fa8      0x00000002
(gdb) info frame 0
Stack frame at 0xbffff190:
 eip = 0x80483e4 in f (s1.c:6); saved eip 0x804841a
 called by frame at 0xbffff1a0
 source language c.
 Arglist at 0xbffff188, args: args=0xbffff3cc "AAAAAAAAA"
 Locals at 0xbffff188, Previous frame's sp is 0xbffff190
 Saved registers:
  ebp at 0xbffff188, eip at 0xbffff18c
(gdb)


It says saved ebp is at 0xbffff188. The value there is 0xbffff198. But
on the other hand, it says "called by frame at 0xbffff1a0". Shouldn't
this be the same value? And it seems like ebp is saved two times, at
0xbffff184 and at 0xbffff188? I would expect the saved eip at
0xbffff188, but it is at 0xbffff18c.
Can someone explain this behaviour, please?

Daniel Hepper

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2005-05-17  8:18 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-05-16 13:03 What happens on the stack when calling a function? (gcc-3.4.3) Daniel Hepper
2005-05-16 13:23 ` Arturas Moskvinas
2005-05-17  8:18   ` Daniel Hepper

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).