//gdb-call-lambda.cpp
#include <iostream>
void do_something(void) {
std::cout << "blah blah" << std::endl;
auto lambda_func = [](void){
std::cout << "in lambda" << std::endl;
return;
};
lambda_func();
std::cout << "..." << std::endl;
return;
}
int main(int argc, char **argv) {
do_something();
return 0;
}
In this example program, if you compile (g++ gdb-call-lambda.cpp --std=c++11 -g) and then run it in gdb (gdb ./a.out), you can have GDB call any "normal" function. Example:
(gdb) break main
Breakpoint 1 at 0x4008e7: file gdb-call-lambda.cpp, line 20.
(gdb) r
Starting program: /home/keithb/dev/mytest/gdb-call-lambda/a.out
Breakpoint 1, main (argc=1, argv=0x7fffffffdfb8) at gdb-call-lambda.cpp:20
20 do_something();
(gdb) call do_something()
blah blah
in lambda
...
However, if you then try to call the lambda:
(gdb) break do_something
Breakpoint 2 at 0x400891: file gdb-call-lambda.cpp, line 5.
(gdb) c
Continuing.
Breakpoint 2, do_something () at gdb-call-lambda.cpp:5
5 std::cout << "blah blah" << std::endl;
(gdb) n
blah blah
12 lambda_func();
(gdb) n
in lambda
14 std::cout << "..." << std::endl;
(gdb) call lambda_func()
Invalid data type for function to be called
GDB kinda freaks out. So my question is thus: how do you call a lambda in GDB? Asking GDB what it expects reveals nothing of interest when compared to a normal function:
(gdb) whatis lambda_func
type = __lambda0
(gdb) whatis do_something
type = void (void)
I went to see if lambda_func has any special members, eg a function pointer to call, akin to std::function and/or std::bind:
(gdb) print lambda_func
$1 = {<No data fields>}
No special members? Okay maybe it's just a glorified function pointer?
(gdb) call ((void (void)) lambda_func)()
Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffdeaf in ?? ()
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(at 0x0x7fffffffdeaf) will be abandoned.
When the function is done executing, GDB will silently stop.
So I'm not even 100% sure what order to pass any arguments or especially captured types.
I tried additionally call lambda_func.operator()(), call lambda_func::operator(), call lambda_func::operator()(), call __lambda0, call __lambda0(), call __lambda0::operator(), call __lambda0::operator()(), all to no avail.
A search on google reveals things about setting breakpoints in lambdas, but nothing on how to call those lambdas from the debugger.
For what it's worth, this is on Ubuntu 14.04 64-bit using g++ 4.8.2-19ubuntu1 and gdb 7.7-0ubuntu3.1
I was expecting call __lambdaX::operator()() works but it doesn't. I think it is related to GCC implementation. I am not sure if there is a better way but this is my workaround solution when I need to call lambda in GDB.
Briefly, GDB has disassemble command and it gives __lambda0::operator()() const as debug information at the call instruction line. Then, convert that address into a function pointer and call it.
Example explains better.
$ g++ -g -std=c++0x lambda.cpp
$ ./a.out
blah blah
in lambda
...
GDB:
$ gdb ./a.out
GNU gdb (GDB) Fedora 7.7.1-13.fc20
Copyright (C) 2014 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 "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...done.
(gdb) b do_something()
Breakpoint 1 at 0x4008a3: file lambda.cpp, line 4.
(gdb) run
Starting program: /home/alper/cplusplus/a.out
Breakpoint 1, do_something () at lambda.cpp:4
4 std::cout << "blah blah" << std::endl;
Missing separate debuginfos, use:
(gdb) n
blah blah
11 lambda_func();
Disassemble do_something
(gdb) disassemble do_something
Dump of assembler code for function do_something():
0x40089b <+0>: push %rbp
0x40089c <+1>: mov %rsp,%rbp
0x40089f <+4>: sub $0x10,%rsp
=> 0x4008a3 <+8>: mov $0x4009fb,%esi
0x4008a8 <+13>: mov $0x601060,%edi
0x4008ad <+18>: callq 0x400750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc#plt>
0x4008b2 <+23>: mov $0x400770,%esi
0x4008b7 <+28>: mov %rax,%rdi
0x4008ba <+31>: callq 0x400760 <_ZNSolsEPFRSoS_E#plt>
0x4008bf <+36>: lea -0x1(%rbp),%rax
0x4008c3 <+40>: mov %rax,%rdi
0x4008c6 <+43>: callq 0x400870 <__lambda0::operator()() const>
0x4008cb <+48>: mov $0x400a05,%esi
0x4008d0 <+53>: mov $0x601060,%edi
0x4008d5 <+58>: callq 0x400750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc#plt>
0x4008da <+63>: mov $0x400770,%esi
0x4008df <+68>: mov %rax,%rdi
0x4008e2 <+71>: callq 0x400760 <_ZNSolsEPFRSoS_E#plt>
0x4008e7 <+76>: nop
0x4008e8 <+77>: leaveq
0x4008e9 <+78>: retq
GDB outputs line callq 0x400870 <__lambda0::operator()() const> so convert 0x400870 into a function pointer and call it.
(gdb) call ((void (*)()) 0x400870)()
in lambda
(gdb) call ((void (*)()) 0x400870)()
in lambda
(gdb) call ((void (*)()) 0x400870)()
in lambda
Note: If GCC inlines the lambda, there is nothing to call. For example, if the example above is compiled with optimisation switch -O3, there is no line with __lambda0::operator()() const in GDB disassemble output.
In a less synthetic use case I had success in calling the lambda by calling it as
call myLambda.operator()(param1,param2)
But in the test case in the OP GDB appears to think the function is inlined — even though it's not. I've reported this as bug 28137.
Note: these methods are not guaranteed to work with C++14 generic lambda.
Case 1. When modifying the source code is allowed
Requires modifying the source code for each individual lambda.
Method 1.1. std::function
Wrap the lambda in a std::function, and explicitly instantiate it.
(it does have an extra performance hit for dynamically allocate memory, but it doesn't matter because this is only the debug build)
If the lambda itself
does not take any local data types as input or return one,
it's possible to explicitly instantiate the whole class
Otherwise, operator() can be instantiated by using it in the program.
#include<iostream>
#include<functional>
template class std::function<int(int)>; // explicit instantiation
int main(){
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
std::function a_function{a};
a_function(1); // implicit instantiation
__builtin_trap();
}
Call in gdb as a_function.operator()(1).
Reference:
C++, STL, GDB: Cannot evaluate function maybe inlined - Stack Overflow
c++ - gdb stl functions still show as inlined after disabling optimizations - Stack Overflow
Method 1.2: store the address of the lambda
Program:
#include<iostream>
int main(){
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
auto a_operator_call=&decltype(a)::operator();
__builtin_trap();
}
Call like this: (requires appropriate compilation option so that value of a_operator_call is not optimized out)
(gdb) print (a.*a_operator_call)(1)
1
$1 = 2
Reference:
c++ - Possible to call inline functions in gdb and/or emit them using GCC? - Stack Overflow
Case 2. When modifying the source code is not allowed
Case 2.1. Global lambda
#include<iostream>
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
int main(){
__builtin_trap();
}
You need to disable xmethod (at least in my current gdb version. I think it's a bug)
and call with the syntax a.operator()(...):
(gdb) print a
$1 = {<No data fields>}
(gdb) print a(1)
Invalid data type for function to be called.
(gdb) print a.operator()(1)
Python Exception <class 'TypeError'> expected string or bytes-like object:
Error while looking for matching xmethod workers defined in Python.
(gdb) disable xmethod
(gdb) print a.operator()(1)
1
$2 = 2
Reference:
#836820 - gdb: Python Exception <class 'TypeError'> expected string or bytes-like object: - Debian Bug report logs
Gentoo Forums :: View topic - [solved] gdb can't print types anymore
Case 2.2. Local lambda
Note: for this method it may be required to compile with -fkeep-inline-functions.
Consider a simple program
#include<iostream>
int main(){
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
__builtin_trap();
}
And see what symbols are generated in the executable:
(gdb) shell nm --demangle ./a.out |grep lambda
000000000001fe4c t main::{lambda(int)#1}::operator()(int) const
000000000001ff5c t main::{lambda(int)#1}::operator int (*)(int)() const
000000000001ff40 t main::{lambda(int)#1}::_FUN(int)
(gdb) print 'main::{lambda(int)#1}::operator()(int) const'
$1 = {int (const struct {...} * const, int)} 0x555555573e4c <operator()(int) const>
(gdb) print 'main::{lambda(int)#1}::operator()(int) const' (a, 1)
No symbol "(null)" in current context.
(gdb) set $c='main::{lambda(int)#1}::operator()(int) const'
(gdb) print (a.*$c)(1)
Non-pointer-to-member value used in pointer-to-member construct
(gdb) print $c(&a, 1)
1
$2 = 2
You can see that the symbol has the type of a function pointer, not a pointer-to-member-function,
so it must be called with normal syntax (although as can be seen in method 1.2 above gdb does support function-to-member dereference)
It's possible (for example with a Python program) to
automatically parse the symbols with lambda in its demangled name
get its type with maintenance print type command
on lambda invocation: get the lambda object's actual type with the maintenance print type command
(ptype alone is insufficient as some different types have identical string representation and considered equal by gdb, see example below)
then match the types against each other to determine the correct symbol.
Failed attempts
Use gdb:
(gdb) print a
$1 = {<No data fields>}
(gdb) ptype a
type = struct {
}
(gdb) ptype $c
type = int (*)(const struct {...} * const, int)
Use Python API:
Unfortunately, for some reason it consider different types with identical definition equal.
For example in this program:
#include<iostream>
int main(){
auto a=[](int x){ std::cout<<x<<'\n'; return x+1; };
auto b=[](int x){ std::cout<<x<<'\n'; return x+2; };
struct{ int x; } c;
struct{ int x; } d;
struct{ int y; } e;
__builtin_trap();
}
in an IPython interactive shell embedded in gdb:
In [1]: gdb.parse_and_eval("a").type == gdb.parse_and_eval("b").type
Out[38]: True
In [39]: gdb.parse_and_eval("c").type == gdb.parse_and_eval("d").type
Out[39]: True
In [40]: gdb.parse_and_eval("c").type == gdb.parse_and_eval("e").type
Out[40]: False
Things that can be tried out (although not necessary in this particular case)
-fno-inline-functions
-fkeep-inline-functions
-gdwarf-5
-g3
__attribute__((used))
-fno-eliminate-unused-debug-types
-fkeep-static-functions
-fkeep-static-consts
Reference:
c++ - Cannot evaluate function -- may be inlined - Stack Overflow
c++ - Function to mangle/demangle functions - Stack Overflow
c++ - Getting mangled name from demangled name - Stack Overflow
compiler construction - What is Linux utility to mangle a C++ symbol name? - Stack Overflow
Debugging Options (Using the GNU Compiler Collection (GCC))
How to make gdb show the original non-mangling function name on disassembly model? - Stack Overflow
https://dwarfstd.org/doc/Debugging%20using%20DWARF.pdf
Related
I'm trying to step through a C++ program. It throws an exception in a lower layer, but I'm unable to have the gdb debugger take me to the catch statement. One workaround is to register catch-points with (gdb) catch catch but it feels clunky to have to remember to always do that, and it will cause the program to halt at each chain of catch-statements in the lower layers.
Here's a repro to illustrate my point. For this program compiled with g++ foo.cpp -g -o foo..
#include <cstdio>
void f() {
throw 42;
}
int main(int argc, char *argv[])
{
try {
throw 42;
//f();
printf("after throw\n");
} catch (...) {
printf("in catch stmt\n");
}
printf("after try-catch stmt\n");
}
.. Gdb works as I expect: When I step over the throw statement, the next statement is the catch.
$ gdb -q foo
Reading symbols from foo...done.
(gdb) start
Temporary breakpoint 1 at 0x912: file foo.cpp, line 9.
Starting program: /home/dannas/foo
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdd88) at foo.cpp:9
9 throw 42;
(gdb) next
12 } catch (...) {
(gdb)
13 printf("in catch stmt\n");
(gdb)
in catch stmt
12 } catch (...) {
But if I replace the throw 42 statement in main with f(), then the program will end if I step over f().
$ gdb -q foo
Reading symbols from foo...done.
(gdb) start
Temporary breakpoint 1 at 0x912: file foo.cpp, line 10.
Starting program: /home/dannas/foo
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdd78) at foo.cpp:10
10 f();
(gdb) n
in catch stmt
after try-catch stmt
[Inferior 1 (process 32522) exited normally]
Versions:
$ gdb --version
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0
Tom Troey says in https://stackoverflow.com/a/34601424/582010:
[...] there is a special debugging marker (either a function or an "SDT
probe", depending on how things were built) in the low-level unwinding
code that is used when an exception is thrown. gdb puts a breakpoint
at this spot. When this breakpoint is hit, gdb examines the target
stack frame of the throw and, if it is above the nexting frame, puts a
temporary breakpoint at the target of the throw.
Question
Is gdb unable to know which line it will execute next? The quote above seems to suggest that gdb makes an effort to figure out which is the next statement based on available catch handlers? Is there a way to make the debugger behave as I expect: "gdb stops at the next executed statement in a function when stepping"?
My library doctest is tested with 200+ builds on travis CI - x86/x64 Debug/Release linux/osx and with a wide range of compilers - from gcc 4.4 to 6 and clang 3.4 to 3.8
All my tests are ran through valgrind and the address sanitizer (also UB sanitizer).
I recently discovered that not all features of ASAN are on by default - for example:
check_initialization_order=true
detect_stack_use_after_return=true
strict_init_order=true
so I enabled them and started getting errors for code like the example below.
int& getStatic() {
static int data;
return data;
}
int reg() { return getStatic() = 0; }
static int dummy = reg();
int main() { return getStatic(); }
compiled with g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010:
g++ -fsanitize=address -g -fno-omit-frame-pointer -O2 a.cpp
and ran like this:
ASAN_OPTIONS=verbosity=0:strict_string_checks=true:detect_odr_violation=2:check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true ./a.out
produces the following error:
==23425==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_globals.cc:255 "((dynamic_init_globals)) != (0)" (0x0, 0x0)
#0 0x7f699bd699c1 (/usr/lib/x86_64-linux-gnu/libasan.so.2+0xa09c1)
#1 0x7f699bd6e973 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0xa5973)
#2 0x7f699bcf2f5c in __asan_before_dynamic_init (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x29f5c)
#3 0x40075d in __static_initialization_and_destruction_0 /home/onqtam/a.cpp:10
#4 0x40075d in _GLOBAL__sub_I__Z9getStaticv /home/onqtam/a.cpp:10
#5 0x40090c in __libc_csu_init (/home/onqtam/a.out+0x40090c)
#6 0x7f699b91fa4e in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x20a4e)
#7 0x4007b8 in _start (/home/onqtam/a.out+0x4007b8)
The same is with g++-6 (Ubuntu 6.1.1-3ubuntu11~12.04.1) 6.1.1 20160511
The error disappears when I do one of these 3 things:
use clang++ (any version) instead of g++
remove the -O2 and use -O0
remove the static in front of dummy
Why is this happening? If it is a bug - is it reported? How to avoid it?
EDIT:
#vadikrobot said that even this: static int data = 0; static int dummy = data; int main() { } produces the problem.
EDIT:
the answer of #ead is correct, however I found a way to circumvent the removal of the static dummy and asan doesn't assert anymore:
int& getStatic() {
static int data = 0;
return data;
}
int __attribute__((noinline)) reg(int* dummy_ptr) { *dummy_ptr = 5; return getStatic() = 0; }
static int __attribute__((unused)) dummy = reg(&dummy);
int main(int argc, char** argv) { return getStatic(); }
This is a problem with the usage of asan by gcc. I don't know enough to say that this is a bug (because all I know comes from reverse engineering), but there is at least some room for improvement for gcc. But also asan could be more robust in its handling of this case.
What goes wrong? For my explanation I would like to take a look at the assembler code of vadikrobot's example and later move to your problem:
static int data = 0;
static int dummy = data;
int main() { }
First we compile without optimization: g++ -O0 -S (here the whole assembler code)
The most important points are:
-There are two globals, for data and dummy integer static variables:
.local _ZL4data
.comm _ZL4data,4,4
.local _ZL5dummy
.comm _ZL5dummy,4,4
-In the section .init_array are noted all functions which are called prior to main. In our case this is _GLOBAL__sub_I_main:
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
-As expected, the global variables are initialized somewhere in _GLOBAL__sub_I_main:
_GLOBAL__sub_I_main:
...
#in this function is the initialization
call _Z41__static_initialization_and_destruction_0ii
...
After establishing that, let's take a look at the optimized version:
The static variables are local and can only be accessed from this translation unit, they are not used here, so they are not used at all and thus are optimized.
There is nothing in the section .init_array, because there is nothing to initialize.
strangely, there is still an unused _GLOBAL__sub_I_main function, which does just nothing. I guess it should be optimized away as well.
Now let's take a look at unoptimized version with -fsanitize=address (full assembler code here):
The most important thing: section .init_array has now more functions which are needed for initialization of the sanitizer, in the end it all results in these important functions being called in this order:
call __asan_init
call __asan_register_globals
call __asan_before_dynamic_init
call __asan_report_store4
call __asan_after_dynamic_init
What is different for optimized version?
-There are no globals (they are optimized away after all), so __asan_register_globals is not called. This is Ok.
-But strangely the section .init_array contains now again the not needed method _GLOBAL__sub_I_main which does not initialize any globals (they are optimized away), but calls __asan_before_dynamic_init:
_GLOBAL__sub_I_main:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $.LC0, %edi
call __asan_before_dynamic_init
...
The problem with this: It seems as if it were not allowed to call __asan_before_dynamic_init without a prior call to __asan_register_globals because some pointer seems to be NULL - your error trace is a failed assertion.
After having established that, let's go to you problem:
static int dummy = reg(); is not used anywhere in this translation unit and thus is optimized away, there are no globals and you will run in the bad case of __asan_before_dynamic_init without __asan_register_globals.
without static, the variable dummy could be used from a different translation unit and thus cannot be optimized away - there are globals and thus __asan_register_globals is called.
why does gcc version prior to 5.0 work? Sadly, they would not optimize unused global static variables away.
What to do?
You should report this problem to gcc.
As a work around, I would do the optimization by hand.
For example:
int& getStatic() {
static int data=0;
return data;
}
and remove the static variable dummy and maybe also the function reg(), if it is not used for other purposes.
This should have been fixed in GCC recently: https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=77396
Let's say I'm running GDB on a complicated piece of templated C++ code, and I want to know what types are in the current instanciation ... for example:
template <typename T>
void foo ()
{
/// I've got a breakpoint in here somewhere.
}
So I hit my breakpoint, and I want to know what type T is. How can I do that?
Just type frame in gdb prompt:
(gdb) frame
The function name along with template argument will be printed in the first line of output. See online doc:
All of these commands end by printing two lines of output describing
the frame. The first line shows the frame number, the function name,
the arguments, and the source file and line number of execution in
that frame. The second line shows the text of that source line.
If your compiler emits proper debugging information for template parameters (for non-variadic templates, gcc has done this for several years), you can just reference T as if it were an ordinary type, e.g. with ptype.
I tried this test program:
template<typename T>
T f()
{
return 23;
}
int main()
{
return f<int>();
}
Compiled with -g and set a breakpoint inside f:
(gdb) b 4
Breakpoint 1 at 0x4005c5: file q.cc, line 4.
(gdb) r
Starting program: /tmp/q
Breakpoint 1, f<int> () at q.cc:4
4 return 23;
(gdb) ptype T
type = int
How can I do that?
This will give you mangled name of the current symbol:
(gdb) info symbol $pc
This will give you demangled name:
(gdb) main demangle symbol_name
(gdb) shell c++filt symbol_name
You could also break using regexp as follows:
(gdb) rbreak foo<.*>()
This can also be applied to templated class methods
(gdb) rbreak MyClass<.*>::my_method()
I'm trying to debug the following simple program:
#include <iostream>
template <class... Args>
void printAll(Args&&... args) {
using swallow = int[];
swallow{0,
(std::cout << args, 0)...
};
}
int main() {
printAll(1, "23", 4);
}
Compiled with gcc 4.9.2 using:
g++ -std=c++11 -g -O0 foo.cxx
And then debugging with gdb 7.9 using:
gdb a.out
(gdb) break foo.cxx:5
Breakpoint 1 at 0x400884: file foo.cxx, line 5.
(gdb) run
Starting program: /..[snip]../a.out
Breakpoint 1, printAll<int, char const (&) [3], int>(int&&, char const (&) [3], int&&) () at foo.cxx:6
6 swallow{0,
(gdb) bt
#0 printAll<int, char const (&) [3], int>(int&&, char const (&) [3], int&&) () at foo.cxx:6
#1 0x0000000000400813 in main () at foo.cxx:12
I'm in the right function, but I have no way to inspect the parameter pack:
(gdb) info args
No arguments.
(gdb) print args
No symbol "args" in current context.
(gdb) inspect args
No symbol "args" in current context.
How do I actually examine the arguments?
Related: Showing values of parameters packs in gdb
There are two problems here; the first is that g++ emits parameter pack debugging information in the DWARF format using the tags DW_TAG_GNU_template_parameter_pack and DW_TAG_GNU_formal_parameter_pack, which gdb does not yet support (PR linked).
Even when this is fixed, we run into another problem, which is that the debugging information g++ emits is broken; it's missing the parameter name (DW_AT_name) (PR linked).
TBH gdb support for C++11 is pretty abysmal (unsurprising as it was effectively abandoned for so long); another near-showstopper bug for C++11 is that it didn't support rvalue references (DW_TAG_rvalue_reference_type) until version 8, printing error messages like <unknown type in /tmp/a.out, CU 0x0, DIE 0x7f>.
The workaround (other than using clang, or an ancient version of g++ that doesn't use the DW_TAG_GNU_template_parameter_pack tags, e.g. 4.4.7) is to use the stabs debugging format with GCC extensions:
g++ -std=c++11 -gstabs+ -O0 foo.cxx
</>
(gdb) s
void printAll<int, char const (&) [3], int>(int, int&&, char const (&) [3], int&&) (i=999, args#0=#0x7fffffffe45c: 1, args#1=..., args#2=#0x7fffffffe458: 4)
at p.cpp:7
7 swallow{0,
(gdb) p 'args#0'
$1 = (int &) #0x7fffffffe45c: 1
I've noticed a strange behaviour of GDB 7.5 on Windows. Consider the following C program:
int foo(void){
int i = 5;
return i;
}
int main(int argc, char** argv){
foo();
return 0;
}
When compiled as either Classic C or C++, the GDB disass foo command gives the same assembly code, as follows:
Dump of assembler code for function foo:
0x00401954 <+0>: push %ebp
0x00401955 <+1>: mov %esp,%ebp
0x00401957 <+3>: sub $0x10,%esp
0x0040195a <+6>: movl $0x5,-0x4(%ebp)
0x00401961 <+13>: mov -0x4(%ebp),%eax
0x00401964 <+16>: leave
0x00401965 <+17>: ret
End of assembler dump.
However, after inserting a breakpoint at the "leave" command, like so: br *0x00401964, running the code up to that line, and attempting to print out the variable i, the executables produced by compiling it as C and C++ behaves differently. The C executable works as expected and prints out $i = 5, while with the C++ executable GDB chokes up and says "no symbol i in current context".
So just out of curiosity I'd like to know if this is a GDB bug or feature? Or is the compiler (GCC) doing something subtly different so that there's something happening between the lines? Thanks.
EDIT:
Well, I don't think it's true the compiler removed the function completely, because breaking at the line before "leave" and printing the value of i does work.
This is neither bug/feature nor a side effect of compiler optimization.
The disassembly clearly is the output of a non-optmized build (i is written
to the stack in foo+6 and reread from stack one step later in foo+13).
While the assembly output of C and C++ is the same in this case, the debug symbol output however is slightly different. The scope of i is more limited in C++. I can only speculate for the reasons. I would guess that this is related to the fact that scoping is more complex in C++ (think of constructors, destructors, exception) and so the C++ part of gcc is stricter on scopes than the C part of gcc.
Details
(I checked everything on a 32-bit build but on a 64-bit Linux with gcc 4.8 and gdb 7.6. While some details will differ on Windows I expect the general mechanics to be the same)
Note that addresses differ in my case.
(gdb) disas foo
Dump of assembler code for function foo:
0x080483ed <+0>: push %ebp
0x080483ee <+1>: mov %esp,%ebp
0x080483f0 <+3>: sub $0x10,%esp
0x080483f3 <+6>: movl $0x5,-0x4(%ebp)
0x080483fa <+13>: mov -0x4(%ebp),%eax
0x080483fd <+16>: leave
0x080483fe <+17>: ret
End of assembler dump.
Technically, foo+0 and foo+1 are the function prologue, foo+3 to foo+13 is the function body, and foo+16 and foo+17 is the function epilogue. So only foo+3 to foo+13 represent the code between { and }. I would say that the C++ version is more correct in saying that i is out of scope before and after the function body.
To see that this is really a matter of debug symbols you can dump out gdb's internals of the debug structures with maintenance print symbols output_file_on_disk. For C it looks like:
block #000, object at 0x1847710, 1 syms/buckets in 0x80483ed..0x804840e
int foo(); block object 0x18470d0, 0x80483ed..0x80483ff
int main(int, char **); block object 0x18475d0, 0x80483ff..0x804840e section .text
block #001, object at 0x18476a0 under 0x1847710, 1 syms/buckets in 0x80483ed..0x804840e
typedef int int;
typedef char char;
block #002, object at 0x18470d0 under 0x18476a0, 1 syms/buckets in 0x80483ed..0x80483ff, function foo
int i; computed at runtime
block #003, object at 0x18475d0 under 0x18476a0, 2 syms/buckets in 0x80483ff..0x804840e, function main
int argc; computed at runtime
char **argv; computed at runtime
While this is C++
block #000, object at 0x1a3c790, 1 syms/buckets in 0x80483ed..0x804840e
int foo(); block object 0x1a3c0c0, 0x80483ed..0x80483ff
int main(int, char**); block object 0x1a3c640, 0x80483ff..0x804840e section .text
block #001, object at 0x1a3c720 under 0x1a3c790, 1 syms/buckets in 0x80483ed..0x804840e
typedef int int;
typedef char char;
block #002, object at 0x1a3c0c0 under 0x1a3c720, 0 syms/buckets in 0x80483ed..0x80483ff, function foo()
block #003, object at 0x1a3c050 under 0x1a3c0c0, 1 syms/buckets in 0x80483f3..0x80483fd
int i; computed at runtime
block #004, object at 0x1a3c640 under 0x1a3c720, 2 syms/buckets in 0x80483ff..0x804840e, function main(int, char**)
int argc; computed at runtime
char **argv; computed at runtime
So the debug symbols for the C++ code distinguish between the whole function (block #002) and the scope of the function body (block #003). This results in your observations.
(And to see that this is really not gdb just handling something wrong you can even analyze the binary with objdump on Linux or dumpbin on Windows. I did it on Linux and indeed it's the DWARF debug symbols that are different :-) )
It's not really a bug or a feature. The compiler is permitted to substitute functionally-equivalent code and generally does so if it can find a better way to do things. The example code is equivalent to doing nothing at all, so the compiler is free to remove it. This leaves the debugger with nothing to debug, which is good since debugging code that does nothing would be a waste of time anyway.