Is it possible to get the value of unused variable using GDB? Is there some configuration for GCC so that the garbage value of the unused variable will be shown not 'optimized out'?
c file:
#include<stdio.h>
void main()
{
int x;
int y;
printf("value of x: %d",x);
}
In the gdb I want to get the garbage value of variable y.
(gdb) run
Starting program: /home/charmae/workspace/AVT/a.out
Breakpoint 1, main () at file4.c:7
7 printf("value of x: %d",x);
(gdb) info locals
x = 2789364
(gdb) p y
$1 = <optimized out>
(gdb) p x
$2 = 2789364
It has nothing to do with GDB. The entity that optimized that variable out is the compiler (probably GCC in your case). You might force it to keep it by declaring the variable as volatile
A better question is - why are you trying to do?
It's nothing to do with gcc. Either the compiler has compiled code to maintain the value, or it hasn't.
You might add an y=y; statement. That would force y to be used, and with gcc -O0 -g keep track of it (at least on my Linux/Debian/Sid/AMD64 with gcc 4.6.2 and gdb 7.3.50)
Related
I know it must've been answered somewhere, but I didn't find any information regarding this strange behavior. I was just messing around with the heap, and when I executed the program with zero optimiziations, there was no error. I went to godbolt and it looks like there's no assembly instructions at all.
What happens to the code when you pass only -O without any level?
What's the difference between this and -O0, that by the way, works well?
int main(int argc, char **argv)
{
char* x = malloc(10);
char n = x[11];
}
$ gcc -O -g -fsanitize=address main.c -o main
$ ./main # No problems
gdb) info locals # Without -fsanitize=address
x = <optimized out>
n = <optimized out>
I went to godbolt and it looks like there's no assembly instructions at all
Indeed, GCC optimized out all your code because you do not use in any way. If you change it slightly then it will stay in generated assembly and AddressSanitizer will find heap overflow.
The following code generates AddressSanitizer heap-buffer-overflow error as you expect:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char* x = malloc(10);
char n = x[11];
return n;
}
What happens to the code when you pass only -O without any level?
It's the same as -O1, "optimize a little bit"
What's the difference between this and -O0, that by the way, works well?
Since your code has no obvious side effects, it will be removed by the optimizer if enabled. This happens no matter if you invoke undefined behavior with x[11] or if you access a valid index x[0]. Writing to the x[0] item first to ensure it isn't indeterminate doesn't have any effect either.
-O0 explicitly disables optimizations, so you'll get some manner of machine code generated... maybe. The compiler doesn't have to generate anything predictable or meaningful in case your code contains undefined behavior.
There's generally no bounds-checking in C, it's the programmer's responsibility to handle it.
As for -fsanitize=address, add a side effect and it might kick in:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char* x = malloc(10);
char n = x[11];
printf("%d\n", n);
}
Results in:
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address-...
The docs answer both of your questions.
What happens to the code when you pass only -O without any level?
-O
-O1
Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function.
[...]
It enables optimizations.
What's the difference between this and -O0
-O0
Reduce compilation time and make debugging produce the expected results. This is the default.
As the default, it really does nothing at all.
In this mode, optimizations (or at least non-trivial ones) are effectively disabled.
Zero optimiziation doesn't detect heap overflow
On Compiler Explorer, you did enable optimizations (by using -O).
If you stop optimizing the variables away (by removing -O or by using -O0), you get the expected error.
<source>: In function 'main':
<source>:7:15: warning: unused variable 'n' [-Wunused-variable]
7 | char n = x[11];
| ^
<source>:7:15: warning: 'x[11]' is used uninitialized [-Wuninitialized]
7 | char n = x[11];
| ^
Program returned: 1
Program stderr
=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001b at pc 0x0000004011a4 bp 0x7fffc6caef80 sp 0x7fffc6caef78
READ of size 1 at 0x60200000001b thread T0
#0 0x4011a3 in main /app/example.c:7
#1 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
#2 0x40109d in _start (/app/output.s+0x40109d)
0x60200000001b is located 1 bytes to the right of 10-byte region [0x602000000010,0x60200000001a)
allocated by thread T0 here:
#0 0x7f88c4e9d1af in malloc (/opt/compiler-explorer/gcc-12.1.0/lib64/libasan.so.8+0xbb1af)
#1 0x401167 in main /app/example.c:6
#2 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
[snip]
Demo on Compiler Explorer
The same would happen with optimizations if the program actually used n.
When I compile the following code with gcc -g -O1 t.c
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
return 0;
}
GCC optimize out all unused local variables. This can be seen by GDB disassemble command, only instructions for return 0 were left:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000401106 <+0>: mov $0x0,%eax
0x000000000040110b <+5>: ret
End of assembler dump.
However, GDB somehow knows the values of variables from C code
(gdb) info locals
a = 1
b = 2
c = 3
d = 4
How does GDB know about these values if they are absent in generated assembly code?
The message that a value was optimized out doesn't mean that a variable got optimized away. It means that the information needed to determine the variable's value is no longer available, possibly because it was overwritten or reused to hold a different variable. Here's an example of that happening:
#include <stdio.h>
int main(int argc, char **argv) {
int x = argc + 42;
getchar();
return 0;
}
Compile that with gcc -g -O3. If you do start and then step, you'll be able to do print x and see its value, even though it's never actually calculated by the program, because the debugging information says how the value would have been calculated. But if you then do next and hit Enter an extra time to let getchar return, now print x will tell you that the value was optimized out. That's because the only copy of argc the program had access to was in rdi, which got clobbered by getchar, and the compiler didn't bother to save its old value first since the program doesn't need it afterwards.
As already noted in comments, GCC stores variable values in debug info. It is possible to see them in GDB because GCC generates extra debug info when doing optimized build. Extra GCC options -fvar-tracking and -fvar-tracking-assignments are implicitly enabled in this case. If disable this extra debug info, GDB outputs <optimized out> as usual.
If to build with gcc -fno-var-tracking -g -O1 t.c, GDB output is:
(gdb) i locals
a = <optimized out>
b = <optimized out>
c = <optimized out>
d = <optimized out>
However if to build without optimization and without var-tracking with gcc -fno-var-tracking -g -O0 t.c, GDB can get values from unoptimized code:
(gdb) i locals
a = 1
b = 2
c = 3
d = 4
The square root of 3, as estimated by Wolfram Alpha:
1.7320508075688772935274463415058723669428052538103806280558...
When I do sqrt(3) in C, it evaluates to 0. Why?
EDIT4: here's how you can reproduce this issue in GDB. Create test.c as follows:
#include <stdio.h>
#include <math.h>
int main()
{
printf("sqrt(3): %f\n", sqrt(3));
return 0;
}
Compile:
gcc -O0 -g -Wall -pedantic -ansi -lm -o test test.c
Run debugger:
gdb test
Enter this at console:
(gdb) break test.c:6
Breakpoint 1 at 0x400578: file test.c, line 6.
(gdb) r
Starting program: /home/pdedecker/Desktop/test
Breakpoint 1, main () at test.c:6
6 printf("sqrt(3): %f\n", sqrt(3));
(gdb) print sqrt(3)
$1 = 0
(gdb) s
sqrt(3): 1.732051
My GDB version is GNU gdb (GDB) SUSE (7.1-3.12).
The problem is not the missing function declaration (which isn't missing, since you did include <math.h>).
The problem is missing debug info for the sqrt you are actually using. Without that debug info, GDB has no clue what parameter type to pass to sqrt(), and what it returns.
You can get the required debug info on many Linux distributions by installing libc-debuginfo package. Here is what I see on such a system:
gdb -q ./a.out
Reading symbols from /tmp/a.out...done.
(gdb) b main
Breakpoint 1 at 0x400558: file t.c, line 6.
(gdb) r
Breakpoint 1, main () at t.c:6
6 printf("sqrt(3): %f\n", sqrt(3));
(gdb) p sqrt
$1 = {<text variable, no debug info>} 0x7ffff7b7fb50 <__sqrt>
Note: "no debug info"
(gdb) p sqrt(3)
$2 = 0
(gdb) p sqrt(3.0)
$3 = 0
Note: matches your behavior.
What sqrt functions do have debug info?
(gdb) info func sqrt
All functions matching regular expression "sqrt":
File ../sysdeps/x86_64/fpu/e_sqrt.c:
double __ieee754_sqrt(double);
File s_csqrt.c:
complex double __csqrt(complex double);
File ../sysdeps/x86_64/fpu/e_sqrtf.c:
float __ieee754_sqrtf(float);
File w_sqrtf.c:
float __sqrtf(float);
File s_csqrtf.c:
complex float __csqrtf(complex float);
File ../sysdeps/i386/fpu/e_sqrtl.c:
long double __ieee754_sqrtl(long double);
File w_sqrtl.c:
long double __sqrtl(long double);
File s_csqrtl.c:
complex long double __csqrtl(complex long double);
File ../sysdeps/ieee754/dbl-64/mpsqrt.c:
void __mpsqrt(mp_no *, mp_no *, int);
File w_sqrt.c:
double __sqrt(double);
(gdb) p __sqrt
$4 = {double (double)} 0x7ffff7b7fb50 <__sqrt>
Note: __sqrt is at the same address as sqrt, but GDB knows its type!
(gdb) p __sqrt(3)
$5 = 1.7320508075688772
(gdb) p __sqrt(3.0)
$6 = 1.7320508075688772
One can reasonably argue this is a bug in GDB. Feel free to create one in GDB bugzilla.
I predict that you didn't do #include <math.h>
Without a function declaration, C will default the return value of a function to int. A floating point number might come back as 0 depending on the size of your int. C will also not know how to convert the function argument. It will default to passing the argument as whatever type it happens to be. If you pass an integer to sqrt() it will not be converted to a double, but the sqrt() function will interpret the bit pattern as double.
To call a function without debug info, you must explicitly tell gdb the type for the return and arguments, using a function pointer cast. So, for your example:
(gdb) print ((double (*) (double)) sqrt) (3)
$1 = 1.7320508075688772
#include <stdio.h>
#include <math.h>
int main()
{
printf("sqrt(3): %f\n", sqrt(3));
return 0;
}
Output:
josh#josh-ubuntu:~/scratch$ ./a.out
sqrt(3): 1.732051
Maybe calling sqrt is not supported ! Maybe because it's a libc function. I don't know the deep reason why, but the following test shows an interesting behaviour:
double mysqrt(double x) { return sqrt(x) };
Then in a gdb session:
(gdb) p mysqrt(3)
$1 = 1.7320508075688772
(gdb) p sqrt(3)
$2 = -1209775368
In code reviews I ask for option (1) below to be used as it results in a symbol being created (for debugging) whereas (2) and (3) do not appear to do so at least for gcc and icc. However (1) is not a true const and cannot be used on all compilers as an array size. Is there a better option that includes debug symbols and is truly const for C?
Symbols:
gcc f.c -ggdb3 -g ; nm -a a.out | grep _sym
0000000100000f3c s _symA
0000000100000f3c - 04 0000 STSYM _symA
Code:
static const int symA = 1; // 1
#define symB 2 // 2
enum { symC = 3 }; // 3
GDB output:
(gdb) p symA
$1 = 1
(gdb) p symB
No symbol "symB" in current context.
(gdb) p symC
No symbol "symC" in current context.
And for completeness, the source:
#include <stdio.h>
static const int symA = 1;
#define symB 2
enum { symC = 3 };
int main (int argc, char *argv[])
{
printf("symA %d symB %d symC %d\n", symA, symB, symC);
return (0);
}
The -ggdb3 option should be giving you macro debugging information. But this is a different kind of debugging information (it has to be different - it tells the debugger how to expand the macro, possibly including arguments and the # and ## operators) so you can't see it with nm.
If your goal is to have something that shows up in nm, then I guess you can't use a macro. But that's a silly goal; you should want to have something that actually works in a debugger, right? Try print symC in gdb and see if it works.
Since macros can be redefined, gdb requires the program to be stopped at a location where the macro existed so it can find the correct definition. In this program:
#include <stdio.h>
int main(void)
{
#define X 1
printf("%d\n", X);
#undef X
printf("---\n");
#define X 2
printf("%d\n", X);
}
If you break on the first printf and print X you'll get the 1; next to the second printf and gdb will tell you that there is no X; next again and it will show the 2.
Also the gdb command info macro foo can be useful, if foo is a macro that takes arguments and you want to see its definition rather than expand it with a specific set of arguments. And if a macro expands to something that's not an expression, gdb can't print it so info macro is the only thing you can do with it.
For better inspection of the raw debugging information, try objdump -W instead of nm.
However (1) is not a true const and cannot be used on all compilers as an array size.
This can be used as array size on all compilers that support C99 and latter (gcc, clang). For others (like MSVC) you have only the last two options.
Using option 3 is preferred 2. enums are different from #define constants. You can use them for debugging. You can use enum constants as l-value as well unlike #define constants.
The square root of 3, as estimated by Wolfram Alpha:
1.7320508075688772935274463415058723669428052538103806280558...
When I do sqrt(3) in C, it evaluates to 0. Why?
EDIT4: here's how you can reproduce this issue in GDB. Create test.c as follows:
#include <stdio.h>
#include <math.h>
int main()
{
printf("sqrt(3): %f\n", sqrt(3));
return 0;
}
Compile:
gcc -O0 -g -Wall -pedantic -ansi -lm -o test test.c
Run debugger:
gdb test
Enter this at console:
(gdb) break test.c:6
Breakpoint 1 at 0x400578: file test.c, line 6.
(gdb) r
Starting program: /home/pdedecker/Desktop/test
Breakpoint 1, main () at test.c:6
6 printf("sqrt(3): %f\n", sqrt(3));
(gdb) print sqrt(3)
$1 = 0
(gdb) s
sqrt(3): 1.732051
My GDB version is GNU gdb (GDB) SUSE (7.1-3.12).
The problem is not the missing function declaration (which isn't missing, since you did include <math.h>).
The problem is missing debug info for the sqrt you are actually using. Without that debug info, GDB has no clue what parameter type to pass to sqrt(), and what it returns.
You can get the required debug info on many Linux distributions by installing libc-debuginfo package. Here is what I see on such a system:
gdb -q ./a.out
Reading symbols from /tmp/a.out...done.
(gdb) b main
Breakpoint 1 at 0x400558: file t.c, line 6.
(gdb) r
Breakpoint 1, main () at t.c:6
6 printf("sqrt(3): %f\n", sqrt(3));
(gdb) p sqrt
$1 = {<text variable, no debug info>} 0x7ffff7b7fb50 <__sqrt>
Note: "no debug info"
(gdb) p sqrt(3)
$2 = 0
(gdb) p sqrt(3.0)
$3 = 0
Note: matches your behavior.
What sqrt functions do have debug info?
(gdb) info func sqrt
All functions matching regular expression "sqrt":
File ../sysdeps/x86_64/fpu/e_sqrt.c:
double __ieee754_sqrt(double);
File s_csqrt.c:
complex double __csqrt(complex double);
File ../sysdeps/x86_64/fpu/e_sqrtf.c:
float __ieee754_sqrtf(float);
File w_sqrtf.c:
float __sqrtf(float);
File s_csqrtf.c:
complex float __csqrtf(complex float);
File ../sysdeps/i386/fpu/e_sqrtl.c:
long double __ieee754_sqrtl(long double);
File w_sqrtl.c:
long double __sqrtl(long double);
File s_csqrtl.c:
complex long double __csqrtl(complex long double);
File ../sysdeps/ieee754/dbl-64/mpsqrt.c:
void __mpsqrt(mp_no *, mp_no *, int);
File w_sqrt.c:
double __sqrt(double);
(gdb) p __sqrt
$4 = {double (double)} 0x7ffff7b7fb50 <__sqrt>
Note: __sqrt is at the same address as sqrt, but GDB knows its type!
(gdb) p __sqrt(3)
$5 = 1.7320508075688772
(gdb) p __sqrt(3.0)
$6 = 1.7320508075688772
One can reasonably argue this is a bug in GDB. Feel free to create one in GDB bugzilla.
I predict that you didn't do #include <math.h>
Without a function declaration, C will default the return value of a function to int. A floating point number might come back as 0 depending on the size of your int. C will also not know how to convert the function argument. It will default to passing the argument as whatever type it happens to be. If you pass an integer to sqrt() it will not be converted to a double, but the sqrt() function will interpret the bit pattern as double.
To call a function without debug info, you must explicitly tell gdb the type for the return and arguments, using a function pointer cast. So, for your example:
(gdb) print ((double (*) (double)) sqrt) (3)
$1 = 1.7320508075688772
#include <stdio.h>
#include <math.h>
int main()
{
printf("sqrt(3): %f\n", sqrt(3));
return 0;
}
Output:
josh#josh-ubuntu:~/scratch$ ./a.out
sqrt(3): 1.732051
Maybe calling sqrt is not supported ! Maybe because it's a libc function. I don't know the deep reason why, but the following test shows an interesting behaviour:
double mysqrt(double x) { return sqrt(x) };
Then in a gdb session:
(gdb) p mysqrt(3)
$1 = 1.7320508075688772
(gdb) p sqrt(3)
$2 = -1209775368