gdb in Windows: different behaviour when debugging compiled C and C++ code - c++

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.

Related

How to prevent std::string being constructed from a nullptr at build time by checking the optimized compiler output

GCC doesn't output a warning when it's optimizer detects std::string being compiled with a nullptr. I found a workaround, just wondering if anything better. It takes advantage of the std::string implementation having an assert that fires when constructed with nullptr. So by compiling with optimisation and looking at the assembly, I can see that line.
Note, below is just an example, this isn't application code.
However, I wondered if there is a better way than what I do. I search for __throw_logic_error with that particular string.
I've pasted this from godbolt.org.
Can be compiled with GCC 12 as follows:
g++ -O1 -S -fverbose-asm -std=c++23 -Wall -o string.s string.cpp
#include <string>
void f()
{
const char * a = nullptr;
std::string s(a);
}
int main()
{
f();
}
.LC0:
.string "basic_string: construction from null is not valid"
f():
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call std::__throw_logic_error(char const*)
main:
sub rsp, 8
call f()
Throwing an exception could be in principle a legal use.
(This is the problem with logic_error's, they shouldn't be exceptions.)
For this reason, I doubt that a compiler will ever complain about this, even if it can evaluate all at compile time.
Your best bet is a static analyzer.
The version of clang-tidy in godbolt, can detect this,
warning: The parameter must not be null [clang-analyzer-cplusplus.StringChecker]
https://godbolt.org/z/h9GnT9ojx
This was incorporated in clang-tidy 17, https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-stringchecker-c
In the lines of compiler warnings you could be lucky (eventually) with something like this, although I wasn't lucky.
void f() {
const char * a = nullptr;
try {
std::string s(a);
}catch(std::logic_error&) {
std::unreacheable(); // or equivalent for current GCC
}
}

errors with g++ 5 and 6 when using address sanitizer and additional asan flags for static initialization order

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

Inserting a comment in __asm results in C2400 error (VS2012)

I was trying to check the compiled assembler of some code in VS 2012. I added two lines (before and after my code) as such:
__asm ; it begins here!
// My code
__asm ; it ends here!
However, VS didn't like that. I got
error C2400: inline assembler syntax error in 'opcode'; found 'bad token'
So I added a NOP, which I didn't want to:
__asm NOP ; Comment!
That worked fine. My question is twofold.
Why didn't VS allow me to add an assembly comment?
Is there a different way to add an assembly comment without adding an instruction, including NOP?
The reason it doesn't work is that __asm is a keyword, just like int is a keyword, it cannot appear by itself and must follow the proper syntax. Take the following bit of code as an example:
int main()
{
int // here's a comment, but it's ignored by the compiler
return 0;
}
The following code will fail with a compilation error, more specifically in VS2012 you get error C2143: syntax error : missing ';' before 'return'. This is an obvious error since we do not have the ending semi-colon to denote end of instruction; add the semi-colon and it compiles fine because we did not dis-obey the syntax of the C (or C++ in this case) language:
int main()
{
int // here's a comment, but it's ignored by the compiler
; // white space and comments are ignored by the compiler
return 0;
}
The same is true of the following code:
int main()
{
__asm ; here's a comment but it's ignored
return 0;
}
Except here we get the error error C2400: inline assembler syntax error in 'opcode'; found 'constant', becuase it's treating everything after the __asm keyword as an assembler instruction and the comment is being rightfully ignored .. so the following code WOULD work:
int main()
{
__asm ; here's a comment but it's ignored
NOP ; white space and comments are ignored by the compiler
__asm {; here's an __asm 'block'
} // outside of __asm block so only C style comments work
return 0;
}
So that answers your first question: Why didn't VS allow me to add an assembly comment?.. because it is a syntax error.
Now for your second question: Is there a different way to add an assembly comment without adding an instruction, including NOP?
Directly, no, there is not, but indirectly, yes there is. It's worth noting that the __asm keyword gets compiled into inline assembly in your program, so comments will be removed from the compiled assembly just as if it were a standard C/C++ comment, so trying to 'force' a comment in your assembly via that method is not necessary, instead, you can use the /FAs compiler flag and it will generate the assembly (machine code) mixed with the source, example:
Given the following (very simple) code:
int main()
{
// here's a normal comment
__asm { ; here's an asm comment and empty block
} // here's another normal comment
return 0;
}
When compiled with the /FAs compiler flag, the file.asm that was produced had the following output in it:
; Listing generated by Microsoft (R) Optimizing Compiler Version 18.00.31101.0
TITLE C:\test\file.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC _main
; Function compile flags: /Odtp
; File c:\test\file.cpp
_TEXT SEGMENT
_main PROC
; 2 : {
push ebp
mov ebp, esp
; 3 : // here's a normal comment
; 4 : __asm { ; here's an asm comment and empty block
; 5 : } // here's another normal comment
; 6 : return 0;
xor eax, eax
; 7 : }
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
Notice how it includes the source and comments. If this code did more, you would see more assembly and the source associated with that as well.
If you're wanting to put comments in the inline assembly itself, then you can use normal C/C++ style comments as well as assembly comments within the __asm block itself:
int main()
{
// here's a C comment
__asm { ; here's an asm comment
// some other comments
NOP ; asm type comment
NOP // C style comment
} // here's another comment
return 0;
}
Hope that can help.
EDIT:
It should be noted the following bit of code also compiles without error and I'm not 100% sure why:
int main()
{
__asm
__asm ; comment
// also just doing it on a single line works too: __asm __asm
return 0;
}
Compiling this code with the single __asm ; comment gives the compilation error, but with both it compiles fine; adding instructions to the above code and inspecting the .asm output shows that the second __asm is ignored for any other assembly commands preceding it. So I'm not 100% sure if this is a parsing bug or part of the __asm keyword syntax as there's no documentation on this behavior.
On Linux, g++ accepts this:
__asm(";myComment");
and outputs, when you run g++ -S -O3 filename.cpp:
# 5 "filename.cpp" 1
;myComment
However, clang++ does not like it, and complains with this, when you run clang++ -S -O3 filename.cpp:
filename.cpp:5:9: error: invalid instruction mnemonic 'myComment'
__asm(";myComment");
^
<inline asm>:1:3: note: instantiated into assembly here
;myComment
^~~~~~~~~
I was, however, able to get both g++ and clang++ to accept:
__asm("//myComment");
which outputs the same comment as in the assembly output above, for both compilers.
What clued me into this, as I was unable to find it anywhere else on the internet, was reading from here:
Microsoft Specific
Instructions in an __asm block can use assembly-language comments:
C++
__asm mov ax, offset buff ; Load address of buff
Because C macros expand into a single logical line, avoid using
assembly-language comments in macros. (See Defining __asm Blocks as C
Macros.) An __asm block can also contain C-style comments; for more
information, see Using C or C++ in __asm Blocks.
END Microsoft Specific
This page then links to here and here. These provide more information on the matter.

Calling a lambda function in GDB

//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

Why isn't my inline assembly in C++ working?

Strangest error output:
#include <iostream>
int main(int arg, char **LOC[])
{
asm
(
"mov eax, 0CF;"
"pusha;"
);
return 0;
}
It complains, and here is the error from GCC:
t.s: Assembler messages:
t.s:31: Error: too many memory references for `mov'
You get this error because your assembly is malformatted. Register accesses are done like %eax, $ is used for immediate operands. Furthermore, GCC, by default (see DanielKO's comment), uses the AT&T syntax, which has the destination on the right and the source on the left. Is this what you are looking for?
mov $0xcf, %eax
Also, your pusha is unbalanced, ie you don't clean up the stack correctly before you return from your function. It would be nice to know what your overall goal is, because right now it seems like you copied and pasted only an incomplete fraction of the source.