Consider following (broken) code:
#include <iostream>
#include <memory>
using namespace std;
class Test {
public:
unique_ptr<string> s;
Test() : s(NULL) {
}
void update(string& st) {
s = unique_ptr<string>(&(st));
}
};
void update(Test& t) {
string s("Hello to you");
t.update(s);
}
int main() {
Test t;
update(t);
cout << *t.s << endl;
}
Here we have error in method Test::update() we do not make a uniq copy of an object. So when the program is run under macOS, you'll get:
$ ./test
Hello t��E]�
test(44981,0x7fff99ba93c0) malloc: *** error for object 0x7fff5d45b690: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
[1] 44981 abort ./test
I've been able to to debug this case successfully using lldb. Even without setting a breakpoint in malloc_error_break, just running application until it gets caught in SIGABRT handler.
lldb ./test
(lldb) target create "./test"
Current executable set to './test' (x86_64).
(lldb) run
Process 44993 launched: './test' (x86_64)
Hello t��_�
test(44993,0x7fff99ba93c0) malloc: *** error for object 0x7fff5fbff680: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Process 44993 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00007fff90d6cd42 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
-> 0x7fff90d6cd42 <+10>: jae 0x7fff90d6cd4c ; <+20>
0x7fff90d6cd44 <+12>: movq %rax, %rdi
0x7fff90d6cd47 <+15>: jmp 0x7fff90d65caf ; cerror_nocancel
0x7fff90d6cd4c <+20>: retq
Target 0: (test) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
* frame #0: 0x00007fff90d6cd42 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff90e5a457 libsystem_pthread.dylib`pthread_kill + 90
frame #2: 0x00007fff90cd2420 libsystem_c.dylib`abort + 129
frame #3: 0x00007fff90dc1fe7 libsystem_malloc.dylib`free + 530
frame #4: 0x0000000100001f7b test`Test::~Test() [inlined] std::__1::default_delete<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::operator(this=0x00007fff5fbff730, __ptr="\a\x94\x99�\x7f\0\0��_�\x7f\0\0\x80�_�\x7f\0\00�_�\x7f\0\00�_�\x7f\0\00�_�\x7f\0\00�_�\x7f\0\00�_�\x7f\0\0��_�\x7f\0\0\x15\x1e\0\0\x01\0\0\0\x80�_�\x7f\0\n0")(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const at memory:2397
frame #5: 0x0000000100001f46 test`Test::~Test() [inlined] std::__1::unique_ptr<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::default_delete<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::reset(this=0x00007fff5fbff730, __p="") at memory:2603
frame #6: 0x0000000100001ef3 test`Test::~Test() [inlined] std::__1::unique_ptr<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::default_delete<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::~unique_ptr(this=0x00007fff5fbff730) at memory:2571
frame #7: 0x0000000100001ef3 test`Test::~Test() [inlined] std::__1::unique_ptr<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::default_delete<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::~unique_ptr(this=0x00007fff5fbff730) at memory:2571
frame #8: 0x0000000100001ef3 test`Test::~Test(this=0x00007fff5fbff730) at main.cpp:6
frame #9: 0x0000000100001e15 test`Test::~Test(this=0x00007fff5fbff730) at main.cpp:6
frame #10: 0x0000000100001ab6 test`main at main.cpp:28
frame #11: 0x00007fff90c3e235 libdyld.dylib`start + 1
Now I see that the problem is in Test destructor, and from here it's a piece of cake.
Unfortunately, trying to debug this case using gdb under macOS was a total failure. Here is what I've done:
$ gdb ./test
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 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-apple-darwin16.7.0".
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 test...Reading symbols from /Users/bazhenov/Developer/linear-counter/tests/test/test.dSYM/Contents/Resources/DWARF/test...done.
done.
(gdb) run
Starting program: /Users/bazhenov/Developer/linear-counter/tests/test/test
[New Thread 0x1403 of process 45204]
warning: unhandled dyld version (15)
Hello tQ�_�
test(45204,0x7fff99ba93c0) malloc: *** error for object 0x7fff5fbff650: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Thread 2 received signal SIGABRT, Aborted.
0x00007fff90d6cd42 in ?? ()
(gdb) bt
#0 0x00007fff90d6cd42 in ?? ()
#1 0x00007fff90e5a457 in ?? ()
#2 0x00007fff5fbff590 in ?? ()
#3 0x0000030700000000 in ?? ()
#4 0x00007fff5fbff590 in ?? ()
#5 0x00007fff5fbff650 in ?? ()
#6 0x00007fff5fbff5a0 in ?? ()
#7 0x00007fff90cd2420 in ?? ()
#8 0xffffffff00000018 in ?? ()
#9 0x00007fff5fbff5b0 in ?? ()
#10 0x00007fffffffffdf in ?? ()
#11 0x00000001000c4000 in ?? ()
#12 0x00007fff5fbff5f0 in ?? ()
#13 0x00007fff90dc1fe7 in ?? ()
#14 0x378b45e65b700074 in ?? ()
#15 0x00007fff99ba00ac in ?? ()
#16 0x0000000000000000 in ?? ()
(gdb)
The question is: why gdb fails to unwind the stack correctly and what options do I have if I need to get correct backtrace using gdb?
why gdb fails to unwind the stack correctly
There are some problems on Mac OS X Sierra with gdb, see this post and gdb bug report.
what options do I have if I need to get correct backtrace using gdb
You can try to downgrade Mac OS (don't know whether is it possible) or try to apply temporary hack patch from above bug report.
Related
I have a code snippet that is behaving weirdly. The code is simply aiming to implement radix and bucket sort. When I comment in the main one of either sort and run it works perfectly. But when I enable both of them i am getting a core dump. And the weird part is core dump as indicated by the stack is crossing over into the stl_vector.h.
The code reference is here:- https://rextester.com/RUUDP10453
When i enable only one of the sorts like below in main it works fine.
//doRadixSort(arr, size);
doBucketSort(arr, size);
or
doRadixSort(arr, size);
//doBucketSort(arr, size);
But when both are enabled there is segmentation fault after both sorts are completed as indicated by the
cout << "i am here at exit" << endl;
The core dump stack indicates some reference/hint at vector of vector buckets. But i have properly allocated and reserved it the required memory. so why this is happening i need some expertise to dig out. I have tried debugging this in eclipse CDT C++ for about 2 hrs with no lead.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 _int_free (av=0x7f66d702eb00 <main_arena>, p=0xf98020, have_lock=0) at malloc.c:3976
3976 >= ((char *) av->top + chunksize(av->top)), 0))
(gdb) where
#0 _int_free (av=0x7f66d702eb00 <main_arena>, p=0xf98020, have_lock=0) at malloc.c:3976
#1 0x00007f66d6cf33dc in __GI___libc_free (mem=<optimized out>) at malloc.c:2966
#2 0x00000000004030fa in __gnu_cxx::new_allocator<std::vector<int, std::allocator<int> > >::deallocate (this=0x7fffc6ffa060, __p=0xf98030) at /usr/include/c++/6.3.1/ext/new_allocator.h:110
#3 0x0000000000402d23 in std::allocator_traits<std::allocator<std::vector<int, std::allocator<int> > > >::deallocate (__a=..., __p=0xf98030, __n=10) at /usr/include/c++/6.3.1/bits/alloc_traits.h:442
#4 0x00000000004027ac in std::_Vector_base<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >::_M_deallocate (this=0x7fffc6ffa060, __p=0xf98030, __n=10)
at /usr/include/c++/6.3.1/bits/stl_vector.h:178
#5 0x00000000004025e4 in std::_Vector_base<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >::~_Vector_base (this=0x7fffc6ffa060, __in_chrg=<optimized out>)
at /usr/include/c++/6.3.1/bits/stl_vector.h:160
#6 0x000000000040211d in std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >::~vector (this=0x7fffc6ffa060, __in_chrg=<optimized out>)
at /usr/include/c++/6.3.1/bits/stl_vector.h:427
#7 0x0000000000401d4b in doBucketSort (arr=0x7fffc6ffa100, size=#0x7fffc6ffa0f8: 12) at tako.cpp:97
#8 0x0000000000401e29 in main (argc=1, argv=0x7fffc6ffa218) at tako.cpp:141
(gdb)
Alternatively, I found the below also works which is equivalent to the resize function.
vector<vector<int>> buckets;
constexpr size_t size=10, bucketSize=10;
buckets.reserve(bucketSize);
for(unsigned int i=0; i<=bucketSize; ++i)
buckets.push_back({ });
for(unsigned int i=0; i<=bucketSize; ++i)
buckets[i].reserve(size);
On macOS 10.12.6, I have a shared library and an executable (both built myself, SIP should not apply AFAIK) and I'd like to preload the library for the executable. It fails, with a return code of 1, and silently otherwise:
$ ./bar/exec
<does stuff>
$ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=./foo/my.dylib ./bar/exec
<returns immediately>
$ echo $?
1
# 1 is not an explicitly returned value from ./bar/exec
If I add either DYLD_PRINT_LIBRARIES or DYLD_PRINT_LIBRARIES_POST_LAUNCH options as documented in man dyld, they both show that both the library and the executable have been loaded successfully:
...
dyld: loaded: /full/path/to/exec
dyld: loaded: ./foo/my.dylib
...
If I try to run the executable with preload in either dtruss or lldb, it runs, without the library preloaded. This might make sense because these tools are SIP-protected.
How do I go about debugging this?
UPDATE
Thanks to #macmoonshine, I am able to reproduce the return code 1 exit under lldb by setting the preload environment from LLDB itself as opposed for LLDB:
(lldb) env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=./foo/my.dylib
(lldb) run
...
Process 24130 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x00007fffc8aac310 libsystem_kernel.dylib`__exit
...
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x00007fffc8aac310 libsystem_kernel.dylib`__exit
frame #1: 0x000000010008ebac my.dylib`my_init at my.c:86 [opt]
frame #2: 0x0000000100018a1b dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 385
frame #3: 0x0000000100018c1e dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
frame #4: 0x00000001000144aa dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 338
frame #5: 0x0000000100013524 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 138
frame #6: 0x00000001000135b9 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 75
frame #7: 0x0000000100005434 dyld`dyld::initializeMainExecutable() + 125
frame #8: 0x00000001000098c6 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3966
frame #9: 0x0000000100004249 dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 470
frame #10: 0x0000000100004036 dyld`_dyld_start + 54
This is a start for debugging.
You can run your code in lldb, where you can set the environment with the env command:
env DYLD_FORCE_FLAT_NAMESPACE=1
env DYLD_INSERT_LIBRARIES=./foo/my.dylib
run
With lldb you can run your program until it crashes. If it exists abnormally with exit() (or _exit()), you should set symbolic breakpoints with b exitand b _exit. So you have the chance to get a backtrace where it fails.
After adding boost syslog into source code, segmentation fault appears inside cpp-netlib library.
I was able to prepare minimum working code snippet to reproduce the problem.
#include <boost/network/protocol/http/client.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/sinks/syslog_backend.hpp>
#include <iostream>
using namespace boost::network;
using namespace boost::network::http;
namespace sinks = boost::log::sinks;
int main()
{
client::request request_("http://www.boost.org/");
client client_;
client::response response_ = client_.get(request_);
std::string body_ = body(response_);
std::cout << "body: " << body_;
using syslog_sinkT = sinks::synchronous_sink <sinks::syslog_backend>;
boost::shared_ptr <sinks::syslog_backend> backend = boost::make_shared <sinks::syslog_backend> ();
boost::shared_ptr<syslog_sinkT> sink = boost::make_shared <syslog_sinkT> (backend);
}
When last 2 lines are commented, segmentation fault disappears and everything works fine.
gdb stack trace (approximately, may vary):
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xf7c29b40 (LWP 19874)]
0x00000000 in ?? ()
(gdb) where
#0 0x00000000 in ?? ()
#1 0x083c376c in boost::asio::detail::task_io_service_operation::complete (bytes_transferred=260, ec=..., owner=...,
this=0xf6900710)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/asio/detail/task_io_service_o$
eration.hpp:38
#2 boost::asio::detail::task_io_service::do_run_one (ec=..., this_thread=..., lock=..., this=<optimized out>)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/asio/detail/impl/task_io_serv$
ce.ipp:372
#3 boost::asio::detail::task_io_service::run (ec=..., this=0x84ea280)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/asio/detail/impl/task_io_serv$
ce.ipp:149
#4 boost::asio::io_service::run (this=0x84e9a94)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/asio/impl/io_service.ipp:59
#5 0x083b5766 in boost::_mfi::mf0<unsigned int, boost::asio::io_service>::operator() (p=<optimized out>, this=<optimized out>)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/bind/mem_fn_template.hpp:49
#6 boost::_bi::list1<boost::_bi::value<boost::asio::io_service*> >::operator()<unsigned int, boost::_mfi::mf0<unsigned int, boost::a$
io::io_service>, boost::_bi::list0> (a=<synthetic pointer>, f=..., this=0x84ea94c)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/bind/bind.hpp:249
#7 boost::_bi::bind_t<unsigned int, boost::_mfi::mf0<unsigned int, boost::asio::io_service>, boost::_bi::list1<boost::_bi::value<boo$
t::asio::io_service*> > >::operator() (this=0x84ea944)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/bind/bind.hpp:1222
#8 boost::detail::thread_data<boost::_bi::bind_t<unsigned int, boost::_mfi::mf0<unsigned int, boost::asio::io_service>, boost::_bi::$
ist1<boost::_bi::value<boost::asio::io_service*> > > >::run (this=0x84ea828)
at /home/kostidov/prj/third_party-master/boost/boost_1_60_0/__public__/v0/Linux-libc6/include/boost/thread/detail/thread.hpp:116
#9 0x0840a4f8 in boost::(anonymous namespace)::thread_proxy (param=0x84ea828) at libs/thread/src/pthread/thread.cpp:167
#10 0xf7de1f70 in start_thread () from /lib/i386-linux-gnu/libpthread.so.0
#11 0xf7d18bee in clone () from /lib/i386-linux-gnu/libc.so.6
Problem exists on Ubuntu 14.04 with cpp-netlib 0.11.2 and both boost versions 1_58_0 and 1_60_0. Boost, cpp-netlib and my application are compiled with -std=c++11.
Note 1. Segmentation fault appears inside cpp-netlib before reaching syslog_backend creation. Only presence of last 2 lines guarantees SIGSEGV reproduction.
Note 2. Reproduces only with syslog_backend. Any other logging targets (file, consol) work fine.
The best idea I have is the problem may lay inside boost during static variables initialisation, but I have no proves regarding this version.
Any suggestions?
Seems like I used too many compile options for building both boost and cpp-netlib.
I prepared new build for both boost and cpp-netlib once again, but this time I used as less additional options as possible.
And it works fine.
EDIT: I found the key which causes the error. It's BOOST_ASIO_ENABLE_HANDLER_TRACKING It was defined during the boost compilation, but wasn't defined during compilation of cpp-netlib and my application.
https://svn.boost.org/trac/boost/ticket/11945
My distribution (Debian) ships debug files in separate packages. So what happens often is that I run a program in gdb until it crashes, in order to obtain a usable backtrace for a bug report. But bt is rather useless, missing the symbol information – because I did not install the corresponding -dbg package.
If I install the package now, is there a way to make gdb search for the symbol files again, without losing my current backtrace?
There is a trick you can use to make gdb try to read symbol files again:
(gdb) nosharedlibrary
(gdb) sharedlibrary
The first command tells it to forget all the symbol information it has, and the second command tells it to re-read it.
I am going to suggest an alternative approach with gdb gcore command, possibly it is suitable for you.
This is gcore description:
(gdb) help gcore
Save a core file with the current state of the debugged process.
Argument is optional filename. Default filename is 'core.<process_id>'
So I have a program that causes a crash:
include <iostream>
int f()
{
time_t curr_ts = time(0);
std::cout << "Before crash " << curr_ts << std::endl;
int * ptr = 0;
*ptr = *ptr +1 ;
std::cout << "After crash " << curr_ts << std::endl;
return *ptr;
}
int main()
{
std::cout << "Before f() " << std::endl;
f();
std::cout << "After f() " << std::endl;
return 0;
}
I compiled it with debug info. However I put the executable with debug info in an archive and for tests use a stripped version.
So it crashes under gdb:
$ gdb ./a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/crash/a.out
Before f()
Before crash 1435322344
Program received signal SIGSEGV, Segmentation fault.
0x000000000040097d in ?? ()
(gdb) bt
#0 0x000000000040097d in ?? ()
#1 0x00000000004009e0 in ?? ()
#2 0x000000314981ed1d in __libc_start_main () from /lib64/libc.so.6
#3 0x00000000004007f9 in ?? ()
#4 0x00007fffffffde58 in ?? ()
#5 0x000000000000001c in ?? ()
#6 0x0000000000000001 in ?? ()
#7 0x00007fffffffe1a9 in ?? ()
#8 0x0000000000000000 in ?? ()
(gdb) gcore crash2.core
Saved corefile crash2.core
I simply generate core file with gcore and leave gdb. Then I get from the archive the version with debug symbols and I can see all symbols:
$ gdb ./a.out ./crash2.core
Reading symbols from ./a.out...done.
warning: exec file is newer than core file.
[New LWP 15215]
Core was generated by `/home/crash/a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040097d in f () at main.cpp:8
8 *ptr = *ptr +1 ;
(gdb) bt
#0 0x000000000040097d in f () at main.cpp:8
#1 0x00000000004009e0 in main () at main.cpp:17
(gdb) info locals
curr_ts = 1435322344
ptr = 0x0
Update
if you set backtrace past-main on you will see at least this __libc_start_main. What is above __libc_start_main is not printed if you analyze only core file (possibly even not saved there) saved wit gcore:
$ gdb ./a.out crash2.core
Reading symbols from ./a.out...done.
warning: exec file is newer than core file.
[New LWP 15215]
Core was generated by `/home/crash/a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040097d in f () at main.cpp:8
8 *ptr = *ptr +1 ;
(gdb) set backtrace past-main on
(gdb) bt
#0 0x000000000040097d in f () at main.cpp:8
#1 0x00000000004009e0 in main () at main.cpp:17
#2 0x000000314981ed1d in __libc_start_main () from /lib64/libc.so.6
Backtrace stopped: Cannot access memory at address 0x4007d0
(gdb)
But if I reproduce the crash under gdb with my test program (with debug info in it) I can see all (see set backtrace past-main on && set backtrace past-entry on):
$ gdb ./a.out
Reading symbols from ./a.out...done.
(gdb) r
Starting program: /home/crash/a.out
Before f()
Before crash 1435328858
Program received signal SIGSEGV, Segmentation fault.
0x000000000040097d in f () at main.cpp:8
8 *ptr = *ptr +1 ;
(gdb) bt
#0 0x000000000040097d in f () at main.cpp:8
#1 0x00000000004009e0 in main () at main.cpp:17
(gdb) set backtrace past-main on
(gdb) set backtrace past-entry on
(gdb) bt
#0 0x000000000040097d in f () at main.cpp:8
#1 0x00000000004009e0 in main () at main.cpp:17
#2 0x000000314981ed1d in __libc_start_main () from /lib64/libc.so.6
#3 0x00000000004007f9 in _start ()
#4 0x00007fffffffde58 in ?? ()
#5 0x000000000000001c in ?? ()
#6 0x0000000000000001 in ?? ()
#7 0x00007fffffffe1a9 in ?? ()
#8 0x0000000000000000 in ?? ()
(gdb)
Is it possible to inspect the return value of a function in gdb assuming the return value is not assigned to a variable?
I imagine there are better ways to do it, but the finish command executes until the current stack frame is popped off and prints the return value -- given the program
int fun() {
return 42;
}
int main( int argc, char *v[] ) {
fun();
return 0;
}
You can debug it as such --
(gdb) r
Starting program: /usr/home/hark/a.out
Breakpoint 1, fun () at test.c:2
2 return 42;
(gdb) finish
Run till exit from #0 fun () at test.c:2
main () at test.c:7
7 return 0;
Value returned is $1 = 42
(gdb)
The finish command can be abbreviated as fin. Do NOT use the f, which is abbreviation of frame command!
Yes, just examine the EAX register by typing print $eax. For most functions, the return value is stored in that register, even if it's not used.
The exceptions to this are functions returning types larger than 32 bits, specifically 64-bit integers (long long), doubles, and structs or classes.
The other exception is if you're not running on an Intel architecture. In that case, you'll have to figure out which register is used, if any.
Here's how todo this with no symbols.
gdb ls
This GDB was configured as "ppc64-yellowdog-linux-gnu"...
(no debugging symbols found)
Using host libthread_db library "/lib64/libthread_db.so.1".
(gdb) break __libc_start_main
Breakpoint 1 at 0x10013cb0
(gdb) r
Starting program: /bin/ls
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Breakpoint 1 at 0xfdfed3c
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
[New Thread 4160418656 (LWP 10650)]
(no debugging symbols found)
(no debugging symbols found)
[Switching to Thread 4160418656 (LWP 10650)]
Breakpoint 1, 0x0fdfed3c in __libc_start_main () from /lib/libc.so.6
(gdb) info frame
Stack level 0, frame at 0xffd719a0:
pc = 0xfdfed3c in __libc_start_main; saved pc 0x0
called by frame at 0x0
Arglist at 0xffd71970, args:
Locals at 0xffd71970, Previous frame's sp is 0xffd719a0
Saved registers:
r24 at 0xffd71980, r25 at 0xffd71984, r26 at 0xffd71988, r27 at 0xffd7198c,
r28 at 0xffd71990, r29 at 0xffd71994, r30 at 0xffd71998, r31 at 0xffd7199c,
pc at 0xffd719a4, lr at 0xffd719a4
(gdb) frame 0
#0 0x0fdfed3c in __libc_start_main () from /lib/libc.so.6
(gdb) info fr
Stack level 0, frame at 0xffd719a0:
pc = 0xfdfed3c in __libc_start_main; saved pc 0x0
called by frame at 0x0
Arglist at 0xffd71970, args:
Locals at 0xffd71970, Previous frame's sp is 0xffd719a0
Saved registers:
r24 at 0xffd71980, r25 at 0xffd71984, r26 at 0xffd71988, r27 at 0xffd7198c,
r28 at 0xffd71990, r29 at 0xffd71994, r30 at 0xffd71998, r31 at 0xffd7199c,
pc at 0xffd719a4, lr at 0xffd719a4
Formatting kinda messed up there, note the use of "info frame" to inspect frames, and "frame #" to navigate your context to another context (up and down the stack)
bt also show's an abbreviated stack to help out.