A segmentation fault occurs when a program tries to access memory outside of the area that has been allocated for it.
In this case, an experienced C programmer can see that the problem is happening in the line where sprintf
is called. But if you can't tell where your segmentation fault is occurring, or if you don't want to bother reading through the code to try to figure it out, then you can build your program with debug symbols (with gcc
, the -g
flag does this) and then run it through a debugger.
I copied your source code and pasted it into a file I named slope.c
. Then I built it like this:
gcc -Wall -g -o slope slope.c
(The -Wall
is optional. It's just to make it produce warnings for more situations. This can help in figuring out what might be wrong, too.)
Then I ran the program in the debugger gdb
by first running gdb ./slope
to start gdb
with the program, and then, once in the debugger, giving the run
command to the debugger:
ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!
Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
(Don't worry about my you have broken Linux kernel i386 NX
... support
message; it doesn't prevent gdb
from being used effectively to debug this program.)
That information is highly cryptic...and if you don't have debug symbols installed for libc, then you'll get an even more cryptic message that has a hexadecimal address instead of the symbolic function name _IO_default_xsputn
. Fortunately, it doesn't matter, because what we really want to know is where in your program the problem is happening.
So, the solution is to look backwards, to see what function calls took place leading up to that particular function call in a system library where the SIGSEGV
signal was finally triggered.
gdb
(and any debugger) has this feature built in: it's called a stack trace or backtrace. I use the bt
debugger command to generate a backtrace in gdb
:
(gdb) bt
#0 0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1 0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2 0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3 0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5 0x08048578 in main () at slope.c:52
(gdb)
You can see that your main
function calls the calc_slope
function (which you've intended), and then calc_slope
calls sprintf
, which is (on this system) implemented with calls to a couple of other related library functions.
What you're generally interested in is the function call in your program that calls a function outside of your program. Unless there is a bug in the library/libraries themselves that you're using (in this case, the standard C library libc
provided by the library file libc.so.6
), the bug that causes the crash is in your program and often will be at or near the last call in your program.
In this case, that's:
#4 0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
That's where your program calls sprintf
. We know this because sprintf
is the next step up. But even without it stating that, you know this because that's what happens on line 26, and it says:
... at slope.c:26
In your program, line 26 contains:
sprintf(s,"%d",curr);
(You should always use a text editor that automatically shows line numbers, at least for the line you are currently on. This is very helpful in interpreting both compile-time errors, and runtime problems revealed while using a debugger.)
As discussed in Dennis Kaarsemaker's answer, s
is a one-byte array. (Not zero, because the value you've assigned it, ""
, is one byte long, that is, it is equal to { '\0' }
, in the same way that "Hello, world!\n"
is equal to { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }
.)
So, why could this still work on some platform (and apparently does when compiled with VC9 for Windows)?
People often say that when you allocate memory and then try to access memory outside of it, that produces an error. But that's not really true. According to the C and C++ technical standards, what this really produces is undefined behavior.
In other words, anything can happen!
Still, some things are more likely than others. Why is it that a small array on the stack will, on some implementations, appear to work like a larger array on the stack?
This comes down to how stack allocation is implemented, which is permitted to vary from platform to platform. Your executable may allocate more memory to its stack than is actually intended to be used at any one time. Sometimes this may allow you to write to memory locations that you have not explicitly laid claim to in your code. It's very likely that this is what's happening when your build your program in VC9.
However, you should not rely on this behavior even in VC9. It could potentially depend on different versions of libraries that could exist on different Windows systems. But even more likely is the problem that the extra stack space is allocated with the intention that it will actually be used, and so it may actually be used. Then you experience the full nightmare of "undefined behavior," where, in this case, more than one variable could end up stored in the same place, where writing to one overwrites the other ...but not always, because sometimes writes to variables are cached in registers and not actually performed immediately (or reads to variables may be cached, or a variable may be assumed to be the same as it was before because the memory allocated to it is known by the compiler not to have been written to through the variable itself).
And that brings me to the other likely possibility for why the program worked when built with VC9. It's possible, and somewhat likely, that some array or other variable was actually allocated by your program (which can include being allocated by a library your program is using) to use the space after the one-byte array s
. So then, treating s
as an array longer than one byte would have the effect of accessing the contents of that/those variables/arrays, which could also be bad.
In conclusion, when you have a mistake like this, it's lucky to get an error like "Segmentation fault" or "General protection fault." When you don't have that, you may not find out until it is too late that your program has undefined behavior.
Best Answer
The bug report is pretty obvious -- see the last two posts, especially.
The problem is that (their) GLIBC is incompatible with the
FORTIFY_SOURCE
option set by default by Ubuntu's gcc.The better solution is to get the latest copy from trunk, where the problem has been fixed; alternately, unset (disable) that option in the makefile or before calling
make
with: