15. Tools for C Programming(gdb and gcc and others)#
In this chapter we will focus on how to use gcc and gdb. I do not care about compilers and debuggers which do not share the idea of freedom in software. Even clang and lldb will not be discussed because they are even bigger threat to software freedom than commercial compilers and debuggers. gdb is a very high-quality debugger and it has very good manual which can be found at http://www.gnu.org/software/gdb/documentation/ and if you feeling upto it then you can go ahead and read the sources of gdb and gcc as well. This chapter is not attempt to replace that manual in any way but rather I will try to present most useful and common operations while debugging. In fact a lot of information in this chapter will come from the documentation of the tools.
Debugging is the process of removing bugs. This warrants another question that what is a bug. A bug is a mistake in a program which compiler cannot catch. Broadly bugs can be categorized in two groups. One which compiler can catch and second which compiler cannot. First category includes syntactical errors, which causes compilation errors and linker errors where linker will throw the error. Second category is called logical errors which can be due to typing mistakes or wrong logic of the program being implemented. While using a debugger like gdb we almost always want to find logical errors although gdb can be used for some other purposes. I will give an example as to what is a logical error.
#include <stdio.h>
int main()
{
int i = 0;
printf("Enter an integer not equal to zero:\n");
scanf("%d", &i);
if(i = 0)
{
printf("Your input is wrong.\n");
}
return 0;
}
Now as you can see that inside if we are assigning 0 to i
while we should
have used ==
operator. Most probable reason for this is a typing
mistake. The worst part is code seems for work for all positive inputs. What
that essentially means that code works correctly i.e. it gives correct output
for positive value but not for 0. Now it is very easy to miss 0 as input if you
are just starting programming. Experienced programmers will definitely check 0
as input since it constitutes boundary test case. Many times in industry 90% of
test cases passing is good enough. As you can see this program will probably
pass 99.99%+ test cases but still it is wrong. A program is not guaranteed to
work unless that has been mathematically proven i.e. formal verification of the
code has been done. But that discussion is for another time.
Even gcc
is not clear about this and a compilation command gcc -
Wall -Wextra filename.c
fails to exactly point the problem because assignment
in if is not illegal in C but yes, you get a hint as given below:
test.c: In function ‘main’:
test.c:10:3: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
if(i = 0)
^
However, if you use -Werror
flag then it will be treated as an error which
is actually a good thing to have.
Another such example is given below:
#include <stdio.h>
int main()
{
int i;
printf("Enter an integer not equal to zero:\n");
scanf("%d", &i);
i=+5;
printf("Increased value of i is %d\n", i);
return 0;
}
Now what possibly has happened here is that programmer intended to use +=
but his/her typing sequence went wrong and s/he types =+
. This is even
worse that previous case because compiler cannot provide any help here and
output will be 5. Once again it is easy to locate because code is small but for
programs of size in tens of thousands to more it would be pain to locate such a
mistake. When such a mistake is made it is easy to locate at that point of time
but say the test case did not cover this and program worked for some time and
then it is mis-behaving. Then it would be very hard for a new programmer to
locate it.
Now the programs presented here are simple enough not to warrant a debugger. However, for complex algorithms it is not so easy to read the code and apply logic and deduce the mistake. Many times we use numbers after performing certain calculation by hand. If that calculation is wrong it is hard to debug it just by reading code.
In these situations we need to use debuggers like gdb
. But before we can
really use a debugger we need to prepare our programs for debugging. To do that
we need to tell compiler i.e. gcc
in our case that we intend to debug a
program bu passing option -g
while compiling. Since now we have touched
compilers I think it is appropriate to discuss some common compiler options.
15.1. Most used gcc options#
gcc
‘s man page is quite extensive and you should spend some time reading
it. Other than that gcc has a a lot of documentation which can be found at
https://gcc.gnu.org/onlinedocs/. I strongly recommend that you read those. Here
I will present some most common options and when to use those. This section is
by no means is a replacement of man page. These options are most used options
and by now you know few of them.
-E
options just preprocesses file. That is#include, #define, #ifdef, #endif
etc are processed. This causes preprocessor to run. This part is handled by cpp which is “The C Preprocessor”. You can do this by runningcpp filename.c
. The output will be preprocessed code which is fed to the compiler for compilation. In case of a problem for example recursive header inclusion the diagnostics is printed and preprocessing terminates.-c
does preprocessing and compilation together. The ourput of previous step i.e. preprocessing is fed to the compiler. Syntactical checks are performed and object code is produced which has the extension.o
. In case of a problem compilation is aborted and diagnostics(errors and warnings) is printed. If this switch is used then linker is not invoked and thus final binary is not generated.-o
filename designates the filename to which output has to be written. You should be careful that output name is not same as your source file else your source file will be oevrwritten.-ansi
can be used to make your code compliant to ANSI or C89 standards. Practically this will make your code highly portable to all the systems where ANSI C compilers are supported.-pedantic
is like-ansi
but this will ensure that your code is compliant to ISO C standards where the specific version can be supplied like-std=c99
. Please look at man page of gcc for more details.-Wall
enables all warnings. Ideally you should have this all the time in your compilation command in whatever build tool you use.-Wextra
enables warnings not enabled by-Wall
and thus-Wall
becomes a misnomer. :)-Werror
makes compiler treat warnings as errors. You can enable this if you are targeting zero warnings. Ideally this should be enabled.-v
prints (on standard error output) the commands executed to run the stages of compilation and compiler information.-O1, -O2, O3
enable optimization for code generation with 1 being lowest and 3 being higehst. Not that this will alter the generated assembly instructions. Thus you should also use next switch.-fopt-info
gives optimization information.-static
prevents linking with the shared libraries if system supports them. On other systems, this option has no effect-shared
produces a shared object which can then be linked with other objects to form an executable. Not all systems support this option. For predictable results, you must also specify the same set of options used for compilation (-fpic, -fPIC
or model suboptions) when you specify this linker option..-Wl,
option passes option to linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example,-Wl,-Map,output.map
passes-Map output.map
to the linker. When using the GNU linker, you can also get the same effect with-Wl,-Map=output.map
.-Idir
puts directory dir in front of list of directories where files included, by#include
, will be searched for.-llib
tell linker that which library to link output with. For example, if we want to link with math library then filename would belibm.so
orlibm
.a then it would look like-lm
. Multiple libraries can be linked like-lname1 -lname2
.-Ldir
puts directory dir in front of list of directories where libraries to be linked, by-l
, will be searched for. Multiple paths can be provided by-Ldir1 -Ldir2
and so on.-g
produce debugging information in the operating system’s native format (stabs, COFF, XCOFF
orDWARF 2
). GDB can work with this debugging information.gcc
allows use to use-g
with-O
. Thus even though code generated may be different than code written it is possible to debug that.-glevel
requests debugging information and also use level to specify how much information. The default level is 2.Level 0 produces no debug information at all. Thus,
-g0
negates-g
.Level 1 produces minimal information, enough for making backtraces in parts of the program that you don’t plan to debug. This includes descriptions of functions and external variables, but no information about local variables and no line numbers.
Level 3 includes extra information, such as all the macro definitions present in the program. Some debuggers support macro expansion when you use
-g3
.-fsanitize=address
enablesAddressSanitizer
, a fast memory error detector. Memory access instructions will be instrumented to detect out-of-bounds and use-after-free bugs. See http://code.google.com/p/address-sanitizer/ for more details.-fsanitize=thread
enables ThreadSanitizer, a fast data race detector. Memory access instructions will be instrumented to detect data race bugs. See http://code.google.com/p/data-race-test/wiki/ThreadSanitizer for more details.-Dname
predefines name as a macro, with definition 1.-Dname=definition
makes sure that contents of definition are tokenized and processed as if they appeared during translation phase three in a#define
directive. In particular, the definition will be truncated by embedded newline characters.If you are invoking the preprocessor from a shell or shell-like program you may need to use the shell’s quoting syntax to protect characters such as spaces that have a meaning in the shell syntax.
If you wish to define a function-like macro on the command line, write its argument list with surrounding parentheses before the equals sign (if any). Parentheses are meaningful to most shells, so you will need to quote the option. With
sh
andcsh
,-D'name(args...)=definition'
works.-D
and-U
(given below) options are processed in the order they are given on the command line. All -imacros
file and-include
file options are processed after all-D
and-U
options.-U
is basically#undef
.-Uname
cancels any previous definition of name, either built in or provided with a-D
option.-fPIC
emits position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table if supported for the target machine. This option makes a difference on the m68k, PowerPC and SPARC. This is very important for building shared libraries.
There are zillions of other useful options, which gcc gives to us. Those
options are used to achieve various results. This is a very small sample of
options which I have described here just to take us forward with our discussion
on gdb
.
15.2. Multi-file programs, static and dynamic libraries#
In real-world programs none of the programs are really one file program but it typically involves really much larger no. of files than that. Similarly, a program is not only executable but part of it is kept as library. Whether the library is static or dynamic that is a different matter. Consider the following famous “Hello, world!” program:
#include <stdio.h>
int main()
{
printf("Hello, world!");
return 0;
}
Now let us compile this program in two different ways as shown below:
$ gcc test.c
shiv@machine ~ $ ls -l a.out
-rwxr-xr-x 1 shiv shiv 8550 Sep 8 22:08 a.out
shiv@machine ~ $ gcc -static test.c
shiv@machine ~ $ ls -l a.out
-rwxr-xr-x 1 shiv shiv 877186 Sep 8 22:08 a.out
Now to understand what has happened here which caused the size of file to blow
by approximately 869k I will use a tool called nm
. If you just invoke
M-x
man followed by nm
then you can read man page of nm
in Emacs
else you can read it in you shell by man nm
. It is a beautiful small piece
of program which lists symbols in an object file. If the argument is not an
object file then it assumes argument is a.out
. Let us invoke nm on the
first a.out
and see what we have.
0000000000601048 B __bss_start
0000000000601048 b completed.6973
0000000000601038 D __data_start
0000000000601038 W data_start
00000000004004c0 t deregister_tm_clones
0000000000400530 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000601040 D __dso_handle
0000000000600e28 d _DYNAMIC
0000000000601048 D _edata
0000000000601050 B _end
0000000000400634 T _fini
0000000000400550 t frame_dummy
0000000000600e10 t __frame_dummy_init_array_entry
0000000000400780 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000000000400418 T _init
0000000000600e18 t __init_array_end
0000000000600e10 t __init_array_start
0000000000400640 R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
w _Jv_RegisterClasses
0000000000400630 T __libc_csu_fini
00000000004005c0 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
000000000040057d T main
U printf@@GLIBC_2.2.5
U puts@@GLIBC_2.2.5
00000000004004f0 t register_tm_clones
0000000000400490 T _start
0000000000601048 D __TMC_END__
That is just 35 lines. The important lines for us are towards bottom where
main
and printf
are located. You can also notice little puts
hiding
there as well. What this means is our code has these functions and they(not
main
) are referring to glibc_2.2.5
shared library. You can dig more
information about what are dependencies in terms of shared libraries by using
another beautiful program ldd
. Again, you should refer to man page of
ldd
. It is described in a manner which shows brevity as ldd
- print
shared library dependencies”. ldd
can be invoked on binaries or shared
libraries. But it cannot be invoked on binaries which have static linking. For
now let us go back and visit our second a.out binary which was produced with
-static
switch to gcc
.
We again invoke nm
on a.out
. And voila!!! Catch your breath. It has
following output which is truncated in between.
0000000000406a00 T abort
00000000006c2240 B __abort_msg
000000000045ebc0 W access
000000000045ebc0 T __access
0000000000490310 t add_fdes
000000000043a960 t add_module.isra.1
000000000045f300 t add_name_to_object.isra.3
00000000006c1c28 d adds.9001
0000000000445b40 T __add_to_environ
00000000004682b0 t add_to_global
0000000000408f40 t adjust_wide_data
00000000006c2740 V __after_morecore_hook
0000000000404980 t alias_compare
0000000000415080 W aligned_alloc
00000000006c27b0 b aligned_heap_area
000000000045e2b0 T __alloc_dir
0000000000499c40 r archfname
00000000006c3300 b archive_stat
00000000006c32e0 b archloaded
00000000006c33b0 b archmapped
0000000000410260 t arena_get2.isra.3
... omitted
... omitted
0000000000433280 W wmempcpy
0000000000433280 T __wmempcpy
000000000045a5c0 T wmemset
0000000000422550 T _wordcopy_bwd_aligned
00000000004226d0 T _wordcopy_bwd_dest_aligned
00000000004222f0 T _wordcopy_fwd_aligned
0000000000422420 T _wordcopy_fwd_dest_aligned
00000000004585b0 T __woverflow
0000000000434130 W write
0000000000434130 W __write
0000000000434139 T __write_nocancel
000000000045ed10 W writev
000000000045ed10 T __writev
00000000004585f0 T __wuflow
00000000004587f0 T __wunderflow
00000000006c1160 D __x86_data_cache_size
00000000006c1170 D __x86_data_cache_size_half
00000000006c3ed8 B __x86_prefetchw
00000000006c1140 D __x86_raw_data_cache_size
00000000006c1150 D __x86_raw_data_cache_size_half
00000000006c1100 D __x86_raw_shared_cache_size
00000000006c1110 D __x86_raw_shared_cache_size_half
00000000006c1120 D __x86_shared_cache_size
00000000006c1130 D __x86_shared_cache_size_half
0000000000433fd0 W _xstat
0000000000433fd0 T __xstat
0000000000433fd0 T __xstat64
0000000000493e60 r yycheck
0000000000493f00 r yydefact
0000000000493efb r yydefgoto
0000000000493ee0 r yypact
0000000000493ed7 r yypgoto
0000000000493f29 r yyr1
0000000000493f1b r yyr2
0000000000493ea0 r yytable
0000000000493f40 r yytranslate
00000000004a8400 r zeroes
00000000004a8560 r zeroes
00000000006c3540 b zone_names
If you are beginner then I doubt your shell is even configured enough to scroll
up and see all the output. Thus for simplicity we you can invoke it as nm
a.out|less
which will redirect the output to the pager less
. If you look
carefully toward bottom you can notice wmemcpy
although not exactly. Now
what is it doing in our code. What has happened is that to satisfy the printf
call the required code has been stuffed in the binary which is causing binary
size to bloat up.
Now let us turn our focus for now on ldd
. If you invoke ldd
then you
will have kind of following output:
$ ldd a.out
linux-vdso.so.1 => (0x00007fffd8b9f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4bb4c18000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4bb4ffe000)
The critical information for us is libc.so.6, which is further a symbolic
link(think shortcut of Windows) to lrwxrwxrwx 1 root root 12 Sep 7 20:54
/lib/x86_64-linux-gnu/libc.so.6 -> libc-2.19.so
. Now let us try ldd
on
-static
version of output and we have following:
$ ldd a.out
not a dynamic executable
If it surprises you then do not be so. This essentially means once we link to library statically then it is no longer dependent on a shared library. Thus you have no output in term of dependency.
Now the time is for judgement call. Should you use shared libraries or should we use static libraries? Usually, shared libraries are better but in case you are not sure if the user of your code will have those libraries on their system then you can give static binaries as well. However, in free software community since we always share code we have the shared libraries available to us(almost always). We will come back to libraries in a bit.
Coming back to multi-file compilation consider two source files and one header file given below:
#ifndef MY_HEADER_H
#define MY_HEADER_H
void f();
#endif
#include "my_header.h"
int main()
{
f();
return 0;
}
#include <stdio.h>
void f()
{
printf("In function f\n");
}
Now assuming these are named my_header.h, test.c
and test1.c
and all
are in same directory then you can compile these like gcc test1.c test.c -o
whatever_name_you_want
then your output will be in that file. However, this
is a bad bad bad way of compiling code. Why because say you change test1.c
where you have written your function f()
then why should you compile
test.c
which has not changed. Thus, the proper way to compile it would be
gcc -c test.c
then gcc -c test1.c
which will produce test.o
and
test1.o
. Then you can link them together using gcc test.o test1.o -o
filename
. Now if you make changes to test1.c
the you need to just compile
test1.c
and link again. This may sound cumbersome for this small set of
files but usually you do not do this manually but rather put it in a
makefile
. You should read about the build tool make
. Describing
make
in this book is not good because it is a build system. You can find
GNU make’s documentation at https://www.gnu.org/software/make/manual/. When you
will compile hundreds of files then you will see why latter method is
superior. In fact, makefiles are not written by hand as I have said at the end
of first chapter but rather a build tool is used like autotools``(``autoconf,
automake, libtool
etc) or cmake
or scons
.
15.3. Creating static and dynamic libraries#
Now that we have learned how to compile multiple files let us turn our focus towards how to create libraries. First let us create static libraries. Again, I will create two files for example and since we are creating libraries we will not have main function. Consider following C files:
#include <stdio.h>
void f()
{
printf("In function f\n");
}
#include <stdio.h>
void g()
{
printf("In function g\n");
}
First we create two object files form these two files by executing commands
gcc -c test.c
and gcc -c test1.c
which will create test.o
and
test1.o
. You are free to choose any other name as well using -o
option
of gcc
. Now we can create three different libraries. libtest.a
or
libtest1.a
which will have individual object code. The tool which we need
to use is ar
. Once again I would request you to see its man page. ar
is
used to create, modify and extract from archives. Now to create you can use a
command like ar rcs libtest.a test.o
and ar rcs libtest1.a test1.o
or
we can even combine both like ar rcs libtest.a test.o test1.o
. Please note
that how lib is prepended to the library name.
Once these libraries are created you need to create header which will have prototype of these functions so that compilation phase of our driver program containing main succeeds. The header may look like:
#ifndef MY_HEADER_H
#define MY_HEADER_H
void f();
void g();
#endif
and then we can write our driver program like:
#include "my_header.h"
int main()
{
f();
g();
return 0;
}
and then compile it like gcc test.c -ltest -L.
since our home directory is
not part of environment variable LIBRARY_PATH
we need to tell linker where
to find the library in this case .
represents the current
directory. However, if you want to add your working directory to ld
which
is our linker then you can export your directory where your file is by export
LIBRARY_PATH=/home/shiv:$LIBRARY_PATH
where /home/shiv
is the directory
containing libtest.a
. Once it is done you can compile without -L
switch.
Building shared library is slightly more involved. Your compilation command
should include -fPIC
even on x86 CPUs like gcc -c -fPIC test.c
and
gcc -c -fPIC test1.c
. Then you can create shared library like gcc -shared
-Wl,-soname,libtest.so.1 -o libtest.so.1.0.1 test.o test1.o
This will produce
a shared library libtest.so.1.0.1
. However, if you do not use -fPIC
option and try to build a shared library then you will get following error:
/usr/bin/ld: test1.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
test1.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
Usually shared libraries have a version in their name in actual files and
symbolic links are created with library names with so extension which stands
for “shared object” in case you have not figure that yet. So we can create a
symbolic link by using the command ln -s /home/shiv/libtest.so.1.0.1
/home/shiv/libtest.so
. Then you can link to it and execute a program like we
did for static one. If you run ldd over this new binary then you will see its
dependencies in terms of shared object. Note that you can even run ldd on
shared objects as well.
15.4. Introduction to gdb#
First thing first. A typical output from invocation of ulimit
command on
64-bit is given below:
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 19721
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 19721
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
Now if you see carefully these describe upper limits on various
parameters. What is unlimited there by default we need not worry about it. Let
us see what is not unlimited and is of importance to me. The very first line of
output is of main concern for me. Core file size of 0 will mean that core file
will not be generated. Another important one is maximum no. of open files. Note
that this may not be a problem for a typical beginner but for advanced
programmers this may be a problem when you are working with a sofware which can
keep lots of files open, for example, Hypertable, which strongly recommends
higher value for this. Another problem is stack size. Usually I like to keep it
32M because 8M is too low. It will allow you as to how deep your recursion is
or how much can you allocate on stack. But it can be easily made more. Now to
increase core file size you can invoke a ulimit option like ulimit -c
unlimited
or you can choose a value if you want.
Consider the following program:
#include <stdio.h>
int main()
{
int *i = (int*)0;
printf("%d\n", *i);
return 0;
}
It is almost guaranteed that this program will cause a SIGSEGV
which should
dump core but core will not be dumped with default settings of ulimit but it
needs a bigger value. If you have changed your core file size upper limit to
some reasonable value then you will see that running this program dumps
core. Core files are very important for debugging an application as it contains
entire contents of memory occupied at the instance it crashes.
To debug a program you should use -g
or -ggdb
while compiling your
program to debug it with gdb
.
Invoking gdb
There are several ways you can invoke gdb
. The simplest is that just use
gdb
command. If you do that you will see something like given below:
$ gdb
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
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-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".
Thus what you see is license information, bug reporting link and documentation
link. But the most important for you, my reader, as a beginner is last two
lines. gdb
has help built-in for its commands. I would come back to this in
a bit let us just finish different ways of invocation first.
However, even before gdb
can start we can get help about what options gdb
accepts. The typical way to get help on a program on GNU/Linux is -h
or
--help
. Please note that -h
does not work always for all commands but
--help
is almost guaranteed to work. Few examples are df
and ls
commands where -h
makes output human-readable. If you invoke gdb -h
then you will have following output(will vary with your version of gdb
):
$ gdb -h
This is the GNU debugger. Usage:
gdb [options] [executable-file [core-file or process-id]]
gdb [options] --args executable-file [inferior-arguments ...]
Selection of debuggee and its files:
--args Arguments after executable-file are passed to inferior
--core=COREFILE Analyze the core dump COREFILE.
--exec=EXECFILE Use EXECFILE as the executable.
--pid=PID Attach to running process PID.
--directory=DIR Search for source files in DIR.
--se=FILE Use FILE as symbol file and executable file.
--symbols=SYMFILE Read symbols from SYMFILE.
--readnow Fully read symbol files on first access.
--write Set writing into executable and core files.
Initial commands and command files:
--command=FILE, -x Execute GDB commands from FILE.
--init-command=FILE, -ix
Like -x but execute commands before loading inferior.
--eval-command=COMMAND, -ex
Execute a single GDB command.
May be used multiple times and in conjunction
with --command.
--init-eval-command=COMMAND, -iex
Like -ex but before loading inferior.
--nh Do not read ~/.gdbinit.
--nx Do not read any .gdbinit files in any directory.
Output and user interface control:
--fullname Output information used by emacs-GDB interface.
--interpreter=INTERP
Select a specific interpreter / user interface
--tty=TTY Use TTY for input/output by the program being debugged.
-w Use the GUI interface.
--nw Do not use the GUI interface.
--tui Use a terminal user interface.
--dbx DBX compatibility mode.
--xdb XDB compatibility mode.
--quiet Do not print version number on startup.
Operating modes:
--batch Exit after processing options.
--batch-silent Like --batch, but suppress all gdb stdout output.
--return-child-result
GDB exit code will be the child's exit code.
--configuration Print details about GDB configuration and then exit.
--help Print this message and then exit.
--version Print version information and then exit.
Remote debugging options:
-b BAUDRATE Set serial port baud rate used for remote debugging.
-l TIMEOUT Set timeout in seconds for remote debugging.
Other options:
--cd=DIR Change current directory to DIR.
At startup, GDB reads the following init files and executes their commands:
* system-wide init file: /etc/gdb/gdbinit
For more information, type "help" from within GDB, or consult the
GDB manual (available as on-line info or a printed manual).
Report bugs to "<http://www.gnu.org/software/gdb/bugs/>".``
This is self-explanatory and I will not attempt to describe it and it is
covered very well in documentation. One more point I would like to give is that
you do not necessarily go to https://gnu.org
for documentation. Rather you should
install gdb-doc
package on your GNU/Linux system. This has an advantage that
your documentation will be in sync with the version of gdb installed on your
system unless you manually install it by source. The help files can be found in
/usr/share/doc/gdb/
by default. Then either you can read the pdf version or
info manual(you can read this in Emacs).
One of the most common ways to invoke gdb
with program name as argument
i.e. gdb prog_name
. You can also specify core file with program name
i.e. gdb prog_name core
and the for program prog_name will be loaded for
debugging. You can also supply PID(process ID, which you get in second column
when you execute ps -eaf
) with prog_name
and if that program with that
PID is running then will allow you to debug that.
There is another way to invoke gdb
and that is gdb --args prog_name
args
which will invoke gdb
on prog_name
with arguments args
.
15.5. Getting help#
Once you are on gdb
prompt after gdb
has initialized you have a lot of
help from gdb
available at your disposal. To see help topics you can type
help
on gdb
prompt and you will see something like below:
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
Thus it is not hard to find if you want to know how to put a breakpoint in a
program using gdb
. help breakpoints
will show you everything related to
breakpoints.