I see many tutorials on gdb asking to use -g option while compiling c program. I fail to understand what does the -g option actually do.
It makes the compiler add debug information to the resulting binaries. This information allows a debugger to associate the instructions in the code with source code files and line numbers. Having debug symbols makes certain kinds of debugging (like stepping through code) much easier, if not possible at all.
The -g option actually has a few tunable parameters, check the manual. Also, it's most useful if you don't optimize the code, so use -O0 or -Og (in newer versions) - optimizations break the connection between instructions and source code. (Most importantly you have to not omit frame pointers from function calls, which is a popular optimization but basically completely ruins the ability to walk up the call stack.)
The debug symbols themselves are written in a standardized language (I think it's DWARF2), and there are libraries for reading that. A program could even read its own debug symbols at runtime, for instance.
Debug symbols (as well as other kinds of symbols like function names) can be removed from a binary later on with the strip command. However, since you'll usually combine debug symbols with unoptimizied builds, there's not much point in that - rather, you'd build a release binary with different optimizations and without symbols from the start.
Other compilers such as MSVC don't include debug information in the binary itself, but rather store it in a separate file and/or a "symbol server" -- so if the home user's application crashes and you get the core dump, you can pull up the symbols from your server and get a readable stack trace. GCC might add a feature like that in the future; I've seen some discussions about it.
Related
All of my programs tend to be rather rudimentary console applications in C. For example, I may write some code to parse a file header and then print some data from the header to the screen. To do this, I would just use functions/symbols from stdio.h stdlib.h, string.h, stdbool.h such as printf(), fopen(), fread(), etc... I usually get away with writing my code in the main.c file as well as several .h files and .c files to go along with them. When it comes time to compile, I will do something like: gcc main.c file1.c file2.c -g -Wall -o my_program
The program runs fine, and with my colleagues, I simply share the source code, or if they're on the same OS, I share the binary and they can typically just either build the code just as I did and run it, or run the binary directly. If my colleague is on a different OS, he/she will just build the source on that machine OR I will build it for them on a machine I have with that OS.
I've never really had to consider how my dependencies were being linked at all in fact. This is probably because I write mostly internal tools and am not releasing to large audiences. That being said, in which situations would the above method fail to run on a system? Is it possible that somebody who has the same version of gcc installed would not be able to just run my executable or build the code themselves, then run it, when I'm only using std C functionality? In fact, I've taken my very same C code from a linux box, copy/pasted it into Visual Studio and compiled with MSVC, and it still works fine with the standard functions... So even cross-compiler, I've not needed to think about the linking yet.
For Linux/Linux compatibility:
Usually, as long as you move to machine which has the same/better glibc (and other libraries that you use), you will not face problem. The glibc (C standard library/runtime) is very good at being backward compatible. It will usually work across distributions. In most cases, you can take your binary to a machine with a minor lower version of the library, and it will work (minor versions are suppose to have only bug fixes, so unless your code trigger bugs it should work).
For Linux/Windows compatibility: in most cases, you will need to recompile, as the libraries runtimes and executable formats are different. Disclaimer: I'm not an expert on this topic.
I've been working on using the Meson build system for an embedded project. Since I'm working on an embedded platform, I've written a custom linker script and also an invocation for the linker. I didn't have any problems until I tried to link in newlib to my project, when I started to have link issues. Just before I got it working, the last error was undefined reference to main which I knew was clearly in the project.
Out of happenstance, I tried adding -mcpu=cortex-m4 to my linker invocation (I am using gcc to link, I am told this is quite typical instead of directly calling ld). It worked! Now, my only question is "why"?
Perhaps I am missing something about how the linking process actually works, but considering I am just producing an ELF file, I didn't think it would be important to specify the CPU architecture to the linker. Is this a newlib thing, or has gcc just been doing magic behind the scenes for me that I haven't seen before?
For reference, here's my project (it's not complete)
In general, you should always link via the compiler driver (link forms of the gcc command), not via direct invocation of ld. If you're developing for bare metal on a particular exact target, it's possible to determine the set of linker arguments you need and use ld directly, but there's a lot that the compiler driver takes care of for you, and it's usually better to let it. (If you don't have a single fixed target, there are unlimited combinations of possibilities and no way you can reproduce all present and future ones someone may care about.)
You can still pass whatever options you like to the linker, e.g. custom linker scripts, via -Wl,... option forms.
As for why the specific target architecture ISA level could matter to linking, linking is not a dumb process of just sticking together binary chunks. Linking can involve patching up (relocations) or even generating (thunks for distant jump targets, etc.) code, in which case the linker may need to care what particular ISA level/variant it's targeting.
Such linker options ensure that the appropriate standard library and start-up code is linked when these are defaulted rather then explicitly specified or overridden.
The one ARM toolchain supports a variety of ARM architecture variants and options; they may be big or little-endian, have various instruction sets - ARM, Thumb. Thumb-2, ARM64 etc, and various extensions such a SIMD or DSP units. The linker requires the architecture information to select the correct library to link for both performance and binary compatibility.
I am using GCC's C compiler for ARM. I've compiled Newlib using the C compiler. I went into the makefile for Newlib and, saw that the Newlib library gets compiled using -g -O2.
When compiling my code and linking against Newlib's standard C library does this debug information get stripped?
You can use -g and -O2 both together. The compiler with optimize the code and keep the debugging information. Of course at some places because of code optimization you will not get information for some symbol that has been removed by code optimization and is no longer present.
From the Gcc options summary
Turning on optimization flags makes the compiler attempt to improve the performance and/or code size at the expense of compilation time and possibly the ability to debug the program.
There are multiple flags and options that will make debugging impossible or difficult. e.g.
-fomit-frame-pointer .... It also makes debugging impossible on some machines.
-fsplit-wide-types.... This normally generates better code for those types, but may make debugging more difficult.
-fweb - ... It can, however, make debugging impossible, since variables no longer stay in a “home register”.
The first two are enabled for -O2.
If you want debugging information to be preserved, the following option can be used.
-Og
Optimize debugging experience. -Og enables optimizations that do not interfere with debugging. It should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
I am working on C/C++ on UNIX and have often seen core files. Many times the core files are difficult to debug to find the actual cause of core or the segmentation fault. Could you please suggest me an efficient debugger?
For segmentation faults, memory leaks, uninitialized data and such, running your program through valgrind is always a good idea. If you are especially interested in memory leaks, the option "--leak-check=full" pays off.
And yes, learn gdb. It takes a little time, but it's worth it.
I think most C compilers on most flavors of *nix support -g to include debugging symbols within the object files, so if you do:
cc -g -c file1.c
cc -g -c file2.c
cc -g file1.o file2.o -o program
./program
Then when you run program if it crashes it should produce a more easily debugged core file. The first two lines just compile source files (producing .o files), the third line tells the compiler to call the linker to link the source files into an executable (passing -g here may not actually do anything if the linker does not have to do anything special to produce an executable with debugging symbols, but it should not hurt anything), and the last line runs the program. You should make sure that you do not tell the compiler to do optimizations when you are trying to debug (unless you find that it does not have errors unless optimizations are turned on) because optimizations typically make the more difficult to follow.
Since I don't know what platform you are on or what tools you have available (or really even what C compiler you are using) so it is difficult to give more specific advice. You should read the man page (manual) for your complier. From the command line type:
man cc
And that should bring up a manual page that tells you lots of things about the compiler on your system. This may tell you how to tell the compiler to produce more warning messages, which could help you find your errors before even running your programs. (note that some warnings may only be produced if you compile with certain optimizations turned on, so even though you probably won't want to debug the optimized program you may want to compile it with optimizations and extra warnings turned on just to see if they tell you anything).
Your Unix system probably has some type of debugger installed. Most Linux machines set up for C development have gdb installed. gdb can be used to run your program in debug mode or to analyze a core file. If you have gdb you can:
gdb ./program
it will start up ready to run your program. If you do:
gdb ./program ./core
it will behave similarly except that it will be as though you were debugging and your program just crashed. From this state the quickest and most helpful thing you can do is to
(gdb) bt
Here (gdb) is the prompt and bt is a command that says to produce a back-trace. That means a call stack, which shows what function the program was in when the failure happened, and what function called that function, and what function called that function, and on and on up to the first function. This can be confusing because it will often show library functions as the most recent called, but this usually means that you have passed in some bad data somewhere along the way that is causing the problem.
gdb is a large and complex program so if it is on your system you should take the time to read up on it.
If it is not on your system then you should find out what similar tools are. Some of the graphical debuggers (either within an IDE or not) act as front ends to command line debuggers and some even support several different command line debuggers, so if you are able to use one of the graphical debuggers you may not actually have to worry about what actual back end command line debugger is being used.
Use gdb. It is the defacto standard Unix C/C++ debugger and as of version 7.0 has reversible debugging features (you can go backwards in time). These reasons alone make it at least worthwhile to check it out.
I really like Totalview. The parallel debugging features are what make me like it as much as I do.
Generally, gdb is an excellent debugger (though it takes a bit to learn). There are also various frontends, some with a GUI, such as DDD or cgdb.
If you explain where specifically you are having trouble, we may be able to better recommend which debugger will help you most.
As suggested above gdb is an excellent debugger. But in the linux terminal debugging larger projects with gdb is little more complex. Simple reason is it is completely command line interface. So i would suggest kdevelop which internally uses the gdb in graphical manner. This debugging tool helped me a lot in debugging my big projects in very easy manner. Let me know if you need any help in using this tool.
When I link to the one under Release/ ,got a fatal error:
LINK : fatal error LNK1146: no argument specified with option '/machine:'
Then I tried to link to the .lib under Debug/ and this time it works.
But what can be different?
Usually, no optimization is done to debug assemblies, while release assemblies are optimized. Debug assemblies will also often contain cruft like source file line numbers.
This isn't actually a C question; it relates to the platforms used.
Frequently, a project/solution will be set up to create a version for debugging and one for release, and putting them in Debug/ and Release/ directories is a common way to distinguish. The debug version will typically compile fast and run slowly, and contain information to link the internal execution to the source code (such as line numbers and variable names). The release version is typically slower to compile and faster to run, and it's much more difficult to track what's going on inside.
Obviously, there have to be differences between the debug and release versions, if only the appropriate compiler flags. However, in the build systems I'm familiar with, it's possible to make arbitrary other changes, and sometimes this will cause a release-version-only bug, which is a pain. Alternately, if the C code doesn't specify the behavior properly, the debug and release versions might interpret it differently, and that's also a pain.
In this case, I'd guess that there was a difference in how they were built. I can't really comment further without more information.
What is the OS? What is the C compiler used? What build system do you use (if you're using an IDE, possibly the one standard with the IDE)? What is the library you're using. Does your organization build it, or do you get it from outside? Knowing these things would give us a clue as to where to start looking.
You may want to change the build configuration for debug and release versions seperately.