I have just shifted to ubuntu and newly using gdb and g++ . Please forgive me if my question is silly .
This is from Richard Stevens Advanced Linux Programming . Three files were created in the folder names reciprocal
main.c:
#include <stdio.h>
#include "reciprocal.hpp"
int main (int argc, char **argv)
{
int i;
i = atoi (argv[1]);
printf ("The reciprocal of %d is %g\n", i, reciprocal (i));
return 0;
}
reciprocal.cpp:
#include <cassert>
#include "reciprocal.hpp"
double reciprocal (int i) {
// I should be non-zero.
assert (i != 0);
return 1.0/i;
}
reciprocal.hpp:
#ifdef __cplusplus
extern "C" {
#endif
extern double reciprocal (int i);
#ifdef __cplusplus
}
#endif
After compiling , I ran the command (gdb) reciprocal and the (gdb) run . I was expecting something as in the book
Starting program: reciprocal
Program received signal SIGSEGV, Segmentation fault.
__strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0)
at strtol.c:287
287 strtol.c: No such file or directory.
(gdb)
But I got :
Starting program: /home/trafalgar/Desktop/reciprocal/reciprocal
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Program received signal SIGSEGV , Segmentation fault.
0x00007ffff7a56ad4 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
What might be happening different . Is this a version problem or anything else ?
Here is the Makefile
reciprocal: main.o reciprocal.o
g++ $(CFLAGS) -o reciprocal main.o reciprocal.o
main.o: main.c reciprocal.hpp
gcc $(CFLAGS) -c main.c
reciprocal.o: reciprocal.cpp reciprocal.hpp
g++ $(CFLAGS) -c reciprocal.cpp
clean:
rm -f *.o reciprocal
How did you compile the program?
use g++ -g programname.c
also, when you do
gdb reciprocal
note if there is a message like
loaded symbols from ...
or
couldnot find symbols
if you get output similar to 2nd code statement, then the problem is that you did not use -g symbol.
You should compile with all warnings and debug info, i.e.
gcc -Wall -g -c main.c
g++ -Wall -g -c reciprocal.cpp
then link with
g++ -g main.o reciprocal.o -o reciprocal
So add
CFLAGS= -Wall -g
in your Makefile. See also this.
Then run the debugger with
gdb reciprocal
then set a program argument with set args 12 command to (gdb) prompt
at last start the debugged program with run when having the (gdb) prompt
Of course, if you don't have any program arguments, argc is 1 and argv[1] is NULL, which you should not pass to atoi(3).
The debugger works quite well. The bug is in your code. You should handle correctly the case when argc is 1 and argv[1] is NULL.
If you encounter a segmentation fault inside a C library function, use the bt or backtrace gdb command to understand how you get there.
Everything is working properly, with expected output.
Compare:
1. Starting program: reciprocal
2. Program received signal SIGSEGV, Segmentation fault.
3. __strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0)
4. at strtol.c:287
5. 287 strtol.c: No such file or directory.
A. Starting program: /home/trafalgar/Desktop/reciprocal/reciprocal
B. warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
C. Program received signal SIGSEGV , Segmentation fault.
D. 0x00007ffff7a56ad4 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
Lines 1-5 are your expected output (from Mr Stevens' text), lines A-D are your actual output.
Lines 1 & A, are essentially identical, they both specify the filename of the executable, (1) is relative pathed, (A) has full path. No worries.
Line B... this is NORMAL, this is gdb telling you that you don't have the debugging information installed for your library functions (NOT YOUR CODE, the dynamically linked libraries on your system).
Line C... Same as (2), easy enough.
Line D... Well, since we don't have debug info for the library functions, it can only point out where the error was as best it can: libc.so.6 (standard library functions, of which strtol is one such)
Essentially, line D is similar to lines 3-5. Without the debug information installed/available, you're not going to get much more information than this.
But everything is working as expected. You're fine.
For help on how to install debug symbols, see here: how-to-use-debug-libraries-on-ubuntu
Fear not! You're doing great. (Technically, the error is on line 6 of your main.cpp, since argv[1] is pretty much undefined because you didn't supply an argument, perhaps confusing since atoi() is often replaced with strtol() behind the scenes.)
Try:
gdb --args ./reciprocal 15
or similar to test with arguments.
Related
When debugging my big rust project, I face a problem that gdb set break point at incorrect line number.
When I type command to set break point at line 406
break <absolute-path-of-my-file>/lib.rs:406
it return that break point is created at line 432
Breakpoint 2 at 0x5555570b9395: file <absolute-path-of-my-file>/lib.rs, line 432.
When I check code at line number 406
list <absolute-path-of-my-file>/lib.rs:406
It return
file: "<absolute-path-of-my-file>/lib.rs", line number: 406, symbol: "???"
My question is:
Why did it set break at line 432 when I created break at line 406?
What does symbol: "???" mean in the list command return? Thanks for any help!
PS: Size of binary file (after build without optimization) is 22 GB, larger than my memory (16GB)
#Phùng Hưng Thịnh
When I compile C/C++ program, I used to use following to debug my program.
g++ -g -Wall source.C
g++ -g -Wall source.CPP
g++ -g -Wall source.c
g++ -g -Wall source.cpp
For debugging I never use the following during compilation:
-O0
Also I used to use:
ulimit -c unlimited
to know if program creating any core dump or not?
If we are using dynamic library(.dll/.so/.sl on any platform windows/Linux/AIX/SunOS/HP-UX/OSF1/IRIX/UNIX) and providing break point to a function inside that external library, that may show ??? output.
If you are providing break to a function inside dynamic library the line numbers will provide the line number where you are invoking that external function from external library.
I have given the sample usage of gdb using C C++ program, related compilation options and external dependencies at any operating systems.
I tried following commands at Linux and "CYGWIN_NT" at windows using gdb:
$ gdb
(gdb) !ls -d .
.
(gdb) quit
Hence can you provide the output for:
(gdb) !ls "<absolute-path-of-my-file>/lib.rs"
GNU DEBUGGER(GDB) usage at Rust
Make sure there is a rust-gdb executable
Use rust-gdb to debug rustc executable.
All my previous comments based only on C and C++ programming not at rust-gdb.
Actually I don't have rust/... debugger at my system.
I have only C C++ compiler and debugger here.
Example C Code:
#include <stdio.h>
void fun()
{
printf( "fun\n");
}
int main()
{
fun();
return 0;
}
Example gdb.exe usage:
$ gcc.exe -g -Wall sample.c -o ./a.out
Exampe gdb
$ gdb.exe -q ./a.out
Reading symbols from ./a.out...
(gdb) break fun
Breakpoint 1 at 0x100401088: file sample.c, line 4.
(gdb) run
Starting program: /cygdrive/c/home/murugesan_openssl/a.out
[New Thread 6108.0xc88]
[New Thread 6108.0x2428]
[New Thread 6108.0x12b8]
Thread 1 "a.out" hit Breakpoint 1, fun () at sample.c:4
4 printf( "fun\n");
(gdb) list
1 #include <stdio.h>
2 void fun()
3 {
4 printf( "fun\n");
5 }
6 int main()
7 {
8 fun();
9 return 0;
10 }
(gdb) quit
A debugging session is active.
Inferior 1 [process 6108] will be killed.
Quit anyway? (y or n) y
Example to print code:
$ gdb -q ./a.out
Reading symbols from ./a.out...
(gdb) break fun
Breakpoint 1 at 0x100401088: file sample.c, line 4.
(gdb) list sample.c:4
1 #include <stdio.h>
2 void fun()
3 {
4 printf( "fun\n");
5 }
6 int main()
7 {
8 fun();
9 return 0;
10 }
(gdb) quit
Using gcc compiler on linux, I have a C program that used command-line argument (one argument) like
./myprog 0
I want to write a makefile that uses conditional compilation so that if I compile like
make SPECIAL=1
then command-line argument is used.
if I compile without SPECIAL like
make
then command-line argument is ignored even if we enter it.
How to make it possible.
I am using following compilation command
gcc -o myprog myprog.c prog2.c prog3.c
The makefile can be trivial but the real work has to happen in the C code. Something like this, as a minimal example:
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
int i;
#ifndef SPECIAL
argc=1;
argv[1] = NULL;
#endif
for(i=1; i<argc; ++i) {
printf(">>%s<<\n", argv[i]);
}
}
Now, you don't even really need a Makefile for this simple program.
bash$ make CFLAGS=-DSPECIAL=1 -f /dev/null myprog
cc -DSPECIAL=1 myprog.c -o myprog
Having said that, making your build nondeterministic by introducing variations which depend on ephemeral build-time whims is a recipe for insanity. Have two separate targets which create two separate binaries, one with the regular behavior, and the other with the foot-gun semantics.
DEPS := myprog.c prog2.c prog3.c # or whatever your dependencies are
myprog: $(DEPS)
myprog-footgun: CLAGS+=-DSPECIAL=1
myprog-footgun: $(DEPS) # same dependencies, different output file
$(CC) $(CFLAGS) -o $# $^
See Target-specific Variable Values in the GNU Make documentation for details of this syntax.
(Notice that Stack Overflow renders tabs in the Markdown source as spaces, so you will not be able to copy/paste this verbatim.)
I would perhaps in fact reverse the meaning of SPECIAL so that it enables the foot-gun version, rather than the other way around (the original version of this answer had this design, just because I had read your question that way originally).
There is a large number of questions on SO about how to execute a library or dynamically load an executable. As far as I can tell, all the answers come down to: compile your executable as position-independent code and load it with dlopen. This worked great --- and still works great on macOS --- until a recent change in glibc, which explicitly disabled dlopening PIEs. This change is now in the current version of glibc (2.30) on ArchLinux, for example, and trying to dlopen a position-independent executable gives an error: "cannot dynamically load position-independent executable".
It's difficult to guess what prompted such a radical change that breaks so much code and useful use cases. (The explanations on Patchwork and Bugzilla don't make much sense to me.) But there is now a question: what to do if you want to create an executable that's also a dynamic library, or vice versa?
A solution was linked from one of the comments. Reproducing it here for posterity:
#include <stdio.h>
#include <unistd.h>
const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-x86-64.so.2";
extern "C" {
void lib_entry(void)
{
printf("Entry point of the service library\n");
_exit(0);
}
}
Compiling with g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry produces a shared object (dynamic library) that can also be executed on Linux.
I have two questions:
What if I want to pass command-line arguments? How to modify this solution so it accepts arc,argv?
Are there other alternatives?
It's difficult to guess what prompted such a radical change
Not really: it never worked correctly.
that breaks so much code
That code was broken already in subtle ways. Now you get a clear indication that it will not work.
Are there other alternatives?
Don't do that?
What problem does dlopening an executable solve?
If it's a real problem, open a GLIBC bugzilla feature request, explaining that problem and requesting a supported mechanism to achieve desired result.
Update:
at least say why "it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?
Thread-local variables is an example that doesn't work correctly. Whether you think they are "real" or not I have no idea.
Here is the code:
// foo.c
#include <stdio.h>
__thread int var;
__attribute__((constructor))
static void init()
{
var = 42;
printf("foo.c init: %d %p\n", var, &var);
}
int bar() {
printf("foo.c bar: %d %p\n", var, &var);
return var;
}
int main()
{
printf("foo.c main: %d %p bar()=%d\n", var, &var, bar());
return 0;
}
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc
foo.c bar: 42 0x7fb5dfd7d4fc
foo.c main: 42 0x7fb5dfd7d4fc bar()=42
// main.c
// Error checking omitted for brevity
#include <dlfcn.h>
#include <stdio.h>
int main()
{
void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
int (*bar)(void) = dlsym(h1, "bar");
printf("main.c: %d\n", bar());
return 0;
}
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c
foo.c bar: 0 0x7fb7305da73c <<< what?
main.c: 0 <<< what?
This is using GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.
Bottom line: this was never designed to work, and you just happened to not step on many of the land-mines, so you thought it is working, when in fact you were exercising undefined behavior.
Please see this answer:
https://stackoverflow.com/a/68339111/14760867
The argc, argv question is not answered there, but when I found I needed one, I hacked something together to parse /proc/self/cmdline at runtime for pam_cap.so use.
There is a large number of questions on SO about how to execute a library or dynamically load an executable. As far as I can tell, all the answers come down to: compile your executable as position-independent code and load it with dlopen. This worked great --- and still works great on macOS --- until a recent change in glibc, which explicitly disabled dlopening PIEs. This change is now in the current version of glibc (2.30) on ArchLinux, for example, and trying to dlopen a position-independent executable gives an error: "cannot dynamically load position-independent executable".
It's difficult to guess what prompted such a radical change that breaks so much code and useful use cases. (The explanations on Patchwork and Bugzilla don't make much sense to me.) But there is now a question: what to do if you want to create an executable that's also a dynamic library, or vice versa?
A solution was linked from one of the comments. Reproducing it here for posterity:
#include <stdio.h>
#include <unistd.h>
const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-x86-64.so.2";
extern "C" {
void lib_entry(void)
{
printf("Entry point of the service library\n");
_exit(0);
}
}
Compiling with g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry produces a shared object (dynamic library) that can also be executed on Linux.
I have two questions:
What if I want to pass command-line arguments? How to modify this solution so it accepts arc,argv?
Are there other alternatives?
It's difficult to guess what prompted such a radical change
Not really: it never worked correctly.
that breaks so much code
That code was broken already in subtle ways. Now you get a clear indication that it will not work.
Are there other alternatives?
Don't do that?
What problem does dlopening an executable solve?
If it's a real problem, open a GLIBC bugzilla feature request, explaining that problem and requesting a supported mechanism to achieve desired result.
Update:
at least say why "it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?
Thread-local variables is an example that doesn't work correctly. Whether you think they are "real" or not I have no idea.
Here is the code:
// foo.c
#include <stdio.h>
__thread int var;
__attribute__((constructor))
static void init()
{
var = 42;
printf("foo.c init: %d %p\n", var, &var);
}
int bar() {
printf("foo.c bar: %d %p\n", var, &var);
return var;
}
int main()
{
printf("foo.c main: %d %p bar()=%d\n", var, &var, bar());
return 0;
}
gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
foo.c init: 42 0x7fb5dfd7d4fc
foo.c bar: 42 0x7fb5dfd7d4fc
foo.c main: 42 0x7fb5dfd7d4fc bar()=42
// main.c
// Error checking omitted for brevity
#include <dlfcn.h>
#include <stdio.h>
int main()
{
void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
int (*bar)(void) = dlsym(h1, "bar");
printf("main.c: %d\n", bar());
return 0;
}
gcc -g main.c -ldl && ./a.out
foo.c init: 42 0x7fb7305da73c
foo.c bar: 0 0x7fb7305da73c <<< what?
main.c: 0 <<< what?
This is using GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.
Bottom line: this was never designed to work, and you just happened to not step on many of the land-mines, so you thought it is working, when in fact you were exercising undefined behavior.
Please see this answer:
https://stackoverflow.com/a/68339111/14760867
The argc, argv question is not answered there, but when I found I needed one, I hacked something together to parse /proc/self/cmdline at runtime for pam_cap.so use.
This question is related to this one as well as its answer.
I just discovered some ugliness in a build I'm working on. The situation looks somewhat like the following (written in gmake format); note, this specifically applies to a 32-bit memory model on sparc and x86 hardware:
OBJ_SET1 := some objects
OBJ_SET2 := some objects
# note: OBJ_SET2 doesn't get this flag
${OBJ_SET1} : CCFLAGS += -PIC
${OBJ_SET1} ${OBJ_SET2} : %.o : %.cc
${CCC} ${CCFLAGS} -m32 -o ${#} -c ${<}
obj1.o : ${OBJ_SET1}
obj2.o : ${OBJ_SET2}
sharedlib.so : obj1.o obj2.o
obj1.o obj2.o sharedlib.so :
${LINK} ${LDFLAGS} -m32 -PIC -o ${#} ${^}
Clearly it can work to mix objects compiled with and without PIC in a shared object (this has been in use for years). I don't know enough about PIC to know whether it's a good idea/smart, and my guess is in this case it's not needed but rather it's happening because someone didn't care enough to find out the right way to do it when tacking on new stuff to the build.
My question is:
Is this safe
Is it a good idea
What potential problems can occur as a result
If I switch everything to PIC, are there any non-obvious gotchas that I might want to watch out for.
Forgot I even wrote this question.
Some explanations are in order first:
Non-PIC code may be loaded by the OS into any position in memory in [most?] modern OSs. After everything is loaded, it goes through a phase that fixes up the text segment (where the executable stuff ends up) so it correctly addresses global variables; to pull this off, the text segment must be writable.
PIC executable data can be loaded once by the OS and shared across multiple users/processes. For the OS to do this, however, the text segment must be read-only -- which means no fix-ups. The code is compiled to use a Global Offset Table (GOT) so it can address globals relative to the GOT, alleviating the need for fix-ups.
If a shared object is built without PIC, although it is strongly encouraged it doesn't appear that it's strictly necessary; if the OS must fix-up the text segment then it's forced to load it into memory that's marked read-write ... which prevents sharing across processes/users.
If an executable binary is built /with/ PIC, I don't know what goes wrong under the hood but I've witnessed a few tools become unstable (mysterious crashes & the like).
The answers:
Mixing PIC/non-PIC, or using PIC in executables can cause hard to predict and track down instabilities. I don't have a technical explanation for why.
... to include segfaults, bus errors, stack corruption, and probably more besides.
Non-PIC in shared objects is probably not going to cause any serious problems, though it can result in more RAM used if the library is used many times across processes and/or users.
update (4/17)
I've since discovered the cause of some of the crashes I had seen previously. To illustrate:
/*header.h*/
#include <map>
typedef std::map<std::string,std::string> StringMap;
StringMap asdf;
/*file1.cc*/
#include "header.h"
/*file2.cc*/
#include "header.h"
int main( int argc, char** argv ) {
for( int ii = 0; ii < argc; ++ii ) {
asdf[argv[ii]] = argv[ii];
}
return 0;
}
... then:
$ g++ file1.cc -shared -PIC -o libblah1.so
$ g++ file1.cc -shared -PIC -o libblah2.so
$ g++ file1.cc -shared -PIC -o libblah3.so
$ g++ file1.cc -shared -PIC -o libblah4.so
$ g++ file1.cc -shared -PIC -o libblah5.so
$ g++ -zmuldefs file2.cc -Wl,-{L,R}$(pwd) -lblah{1..5} -o fdsa
# ^^^^^^^^^
# This is the evil that made it possible
$ args=(this is the song that never ends);
$ eval ./fdsa $(for i in {1..100}; do echo -n ${args[*]}; done)
That particular example may not end up crashing, but it's basically the situation that had existed in that group's code. If it does crash it'll likely be in the destructor, usually a double-free error.
Many years previous they added -zmuldefs to their build to get rid of multiply defined symbol errors. The compiler emits code for running constructors/destructors on global objects. -zmuldefs forces them to live at the same location in memory but it still runs the constructors/destructors once for the exe and each library that included the offending header -- hence the double-free.