gdb not loading symbols when running standalone shared library - c

I have a PIC shared library which also has a main function
#include <dtest2.h>
#include <stdio.h>
extern const char elf_interpreter[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
int dtestfunc1(int x,int y) {
int i=0;
int sum = 0;
for(i=0;i<=x;i++) {
sum+=y;
sum+=dtestfunc2(x,y);
}
return sum;
}
int main (int argc, char const* argv[])
{
printf("starting sharedlib main\n");
int val = dtestfunc1(4,5);
printf("val = %d\n",val);
_exit(0);
}
This library is linked to another shared library libdtest2 which has the implementation of dtestfunc2 called from dtestfunc1.
If i run gdb directly on dtestfunc1 using
gdb libdtest1.so
the symbols for libdtest2.so are not loaded by gdb. I cannot step into dtestfunc1 because of this and if i press s, the function just executes and gets out.
If i create a driver program which calls dlopen on the shared library, gdb loads the symbols properly after dlopen is executed and everything works fine.
Why is gdb behaving differently in both these cases?
How can i manually point gdb to the shared library if i am running gdb directly on the shared library?
Note: This is a toy example that mirrors my problem for a much larger shared library. All my binaries and libraries are compiled with the -ggdb3 flag.
Edit: My shared library is runnable. I added the proper interpreter path using the extern definition in the source code. I compiled it with gcc -ggdb3 -O0 -shared -Wl,-soname,libdtest1.so.1 -ldtest2 -L/usr/lib -Wl,-e,main -o libdtest1.so.1.0 dtest1.o.
I can run it and it executes perfectly.Running the shared library is not the issue here.

Why is gdb behaving differently in both these cases?
Because you didn't build libdtest1.so correctly for GDB to work with it.
In particular, your libdtest1.so is lacking DT_DEBUG entry in its dynamic section, which you can confirm like so:
readelf -d libdtest1.so | grep DEBUG
You should see nothing. In a correctly built runnable libdtest1.so (which you can build using -pie flag), the output should look like this:
0x00000015 (DEBUG) 0x0
The runtime loader updates DT_DEBUG to point to its r_debug structure, which then allows GDB to find other loaded shared libraries. Without DT_DEBUG, GDB can't find them.
Update:
After adding the pie flag my build command is gcc -ggdb3 -O0 -pie -shared -Wl,-soname,libdtest1.so.1 -ldtest2 -L/usr/lib -Wl,-e,main -o libdtest1.so.1.0 dtest1.c -I. The DEBUG section is still missing
Terminology: it's not a DEBUG section. It's a DT_DEBUG entry in the .dynamic section.
It is still missing because -shared overrides -pie. Remove -shared from the link line.
You would also not need -Wl,-e,main, nor would you need to specify the .interp -- GCC will do that for you.
The correct link command:
gcc -ggdb3 -O0 -pie -rdynamic dtest1.c -I. -Wl,-soname,libdtest1.so.1 \
-L/usr/lib -ldtest2 -o libdtest1.so.1.0
(Order of sources and libraries on the link line matters, yours is wrong.)
Added bonus: your main will receive correct argc and argv[], instead of bogus values you are getting now.

the easy fix.... recompile the library with (for gcc) the parameter '-ggdb' Then it will have all the symbols available to gdb.

Related

GCC link against .so file without souce code

I am trying to compile compile a simple "hello world" program for an Axis A210 (cris architecture). I managed to get download GCC from the vendor, but it came with glibc, and the camera is running uClibc-0.9.27. I pulled the file /lib/libuClibc-0.9.27.so from the device.
I managed to compile this program that segfaults:
#include <unistd.h>
int main(int argc, char** argv)
{
*((unsigned int*)0) = 0xDEAD;
}
and this program that just hangs:
#include <unistd.h>
int main(int argc, char** argv)
{
int a = 0;
}
with cris-gcc -g -static -nostdlib -o compiled main.c.
Now I'd like to use the functions in libuClibc, but I can't seem to get the linking to work: I've tried
cris-gcc -g -static -nostdlib -o compiled main.c -luClibc-0.9.27 -L.
but that just gives:
./libuClibc-0.9.27.so: could not read symbols: Invalid operation
collect2: ld returned 1 exit status
Is there a way to link to this .so file or to otherwise get some standard functions like exit working?
regarding:
cris-gcc -g -static -nostdlib -o compiled main.c -luClibc-0.9.27 -L.
The linker works with libraries in the order they are encountered. So they must be listed in the order needed.
The linker needs to know where the library is located before knowing which library to examine. Suggest:
cris-gcc -g -static -nostdlib -o compiled main.c -L. -luClibc-0.9.27
However, a *.so library is NOT a static library. It is a dynamic library, so the option: -static should be removed However, that requires that the dynamic library be available at 'run time' if the related *.a (a static library) is available then it should be used in the compile/link statement.
Note: the function: exit() has its' prototype exposed via the stdlib.h header file, not the unistd.h header file.
regarding:
#include <unistd.h>
int main(int argc, char** argv)
{
*((unsigned int*)0) = 0xDEAD;
}
the parameters: argc and argv are not used, so the compiler will output two warning statements about 'unused parameters'. Suggest using the function signature: int main( void )
this code is trying to write to address 0. However, the application does not 'own' address 0, (an usually, such an address will be 'marked' as 'readonly' so the application will exit with a 'seg fault event')
it is poor programming practice to include header files those contents are not used. Suggest removing the statement: #include <unistd.h>
this statement: int a = 0; will result in the compiler outputting a warning message about a variable that is 'set' but never 'used'
regarding:
cris-gcc -g -static -nostdlib -o compiled main.c -L. -luClibc-0.9.27
When compiling, should always enable the warnings, then fix those warnings. Suggest:
cris-gcc -Wall -Wextra -Wconversion -pedantic -std=c99 -g -static -nostdlib -o compiled main.c -luClibc-0.9.27 -L.
Apart of all the problems noticed by #user3629249 in his answer (all of them are to be followed), the message:
./libuClibc-0.9.27.so: could not read symbols: Invalid operation
collect2: ld returned 1 exit status
means that the libuClibc-0.9.27.so binary has been stripped its symbols or you have not privileges to read the file, and so, the symbol table. The linker is unable to use that binary and it can only be loaded into memory. Anyway, you need a nonstripped shared object, and as suggested by #user3629249, don't use -static (by the reason stated in his answer), put the parameters in order (library dir before the library to be linked, also stated by him). Even you can link the shared by specifying it as:
cris-gcc -nostdlib -o compiled main.c libluClibc-0.9.27.so
and another thing: You need not only the standard C library to link an executable... you normally use a crt0.o at the beginning of your program with the C runtime and the start code for your program. You have not included that, and probably the compiler is getting it from another place.
One question: If you got the compiler, why do you intend to supply your own version of the standard library? isn't provided by the compiler? If you change the libc, then you must change also the crt0.o file. It defaults to some compiler provided, and you haven't received the message no definition for start.
Try to compile with just a main function, as you did, but don't specify shared libraries or directories... just the main code:
cris-gcc -o compiled main.c
and see what happens.... this will be very illustrative of what you lack in your system.

Couldnot execute the dynamically linked program

Hi I tried doing static library and a shared library with the gnu compiler, here is following code
following is the code for the library
calc_mean.c
double mean(double a, double b){
return (a+b)/2;
}
following is my header file calc_mean.h
double mean(double,double);
Now i started creating static library using following commands
first , calc_mean.c is turned into an object file
gcc -c calc_mean.c -o calc_mean.o
second ,the archiver (ar) is invoked to produce a static
library (named libmean.a) out of the object file calc_mean.o
ar rcs libmean.a calc_mean.o
third, created shared library before that using -fPIC option
created an independant code which is necessary for shared library
gcc -c -fPIC calc_mean.c -o calc_mean.o
now the shared library is created using following command line
gcc -shared -Wl,-soname,libmean.so.1 -o libmean.so.1.0.1 calc_mean.o
finally my main.c file that uses the library is as follows
#include <stdio.h>
#include "calc_mean.h"
int main(int argc, char* argv[]){
double v1,v2,m;
v1 = 5.2;
v2 = 7.9;
m=mean(v1,v2);
printf("The mean of %3.2f and %3.2f is %3.2f\n",v1,v2,m);
return 0;
}
finally I linked the program against static library that generated a statically_linked.exe
gcc -static main.c -L. -lmean -o statically_linked
when dynamically linked, it generated a dynamically_linked.exe with the following command
gcc main.c -o dynamically_linked -L. -lmean
Now when i use the command to execute the dynamically linked program using the following command, Iam getting an error message saying LD_LIBRARY is not recognized as an internal or external command,operable program or batch file
LD_LIBRARY_PATH=.D:\c\project3./dynamically_linked
how can I execute the dynamically linked program?
Your last line suggests that you do all this on windows. But you seem to have followed a step by step guide for linux or another *nix platform.
On windows, a dynamically linked library is in .dll format (not .so) and there's no versioning convention, so the command to create the shared library should look a little different.
Instead of:
gcc -shared -Wl,-soname,libmean.so.1 -o libmean.so.1.0.1 calc_mean.o
do the following on windows:
gcc -shared -Wl,--out-implib,libmean.dll.a -o mean-1.dll calc_mean.o
This creates the library itself named mean-1.dll as well as an import library named libmean.dll.a. The import library (sometimes called .lib instead of .dll.a) on the windows platform is just a stub used during linking of your program, AFAIK MinGW doesn't need it, but other compilers could.
Linking your main program should then work with the same command:
gcc main.c -o dynamically_linked -L. -lmean
And for finding .dll libraries in windows, there is no LD_LIBRARY_PATH -- windows just looks for them in the standard search path, including the directory of the .exe requiring it. IOW, you should be able to just run your program.

How does a C static library work?

What code goes into the final executable when using a library?
As an example, we have two files:
/*main.c*/
int main (int argc, char* argv[]){
fc(1); /*This function is defined in fc.c*/
}
Another file:
/*fc.c*/
int fc(int x){
return fe(x);
}
int fe(int y){
return y + 1;
}
We compile fc.c:
gcc -c fc.c
We then get fc.o.
Now lets build a library named test:
ar rcs libtest.a fc.o
We now have libtest.a.
Now we compile main.c
gcc -c main.c
And we obtain main.o
Let's link our main.o to our libtest.a
gcc -L. main.o -ltest
We get the desired a.out
Checking it's symbols:
nm a.out
In between all the symbols, we find:
080483cc T fc
080483df T fe
Seems good.
BUT!
If our main.c changes for this?
/*main.c*/
int main (int argc, char* argv[]){
fe(1); /*This function is defined in fc.c*/
}
After compiling main.c and linking the new main.o to our library, I will still find a symbol for fc. But I don't need that code.
Questions
-Shouldn't the library "give me" only the code I need in main.c?
-Do the functions need to be in separate modules before being added to the library?
-What if I had 300 functions? Would I need to make 300 modules?
Yes, place each function in a separate module. That way the linker will link in only the items needed.
In short, there are compiler flags to prune unused functions from the final executable code, however they are not enabled by default.
GCC can do this "garbage collection" of unused functions if these flags are added:
-ffunction-sections as a compile-time flag. It instructs the compiler to create a separate section (see object file format) for each function. There's also -fdata-sections flag with similar meaning that works for variables.
-Wl,--gc-sections as a link-time flag. The -Wl part instructs GCC to pass the following options to the linker. --gc-sections means "garbage select sections from which all code is unsed". Since due to the compile-time options each function has got a separate section, it effectively performs function-level pruning.

Ubuntu C program loadable module and undefined symbols

Very new to linux in general and trying to build a loadable module for use in zabbix, which works, but trying to build a simple shell program for testing it. That means this module needs to be loaded dynamically.
Sharable module SNMPmath is built with this:
gcc -shared -o SNMPmath.so $(CFLAGS) -I../../../include -I/usr/include/libxml2 -fPIC SNMPmath.c
That works fine for zabbix.
The test program (TestSO.c) uses
lib_handle = dlopen("./SNMPmath.so", RTLD_NOW);
to load this image dynamically, and when it does, it is missing symbols including init_snmp which come from the net-snmp package which is referenced in the SNMPmath loadable module.
My question is both general and specific. What's the right approach -- is this library called by the loadable module supposed to be forced into the loadable module? Is it supposed to be forced into the test program (despite having no compile-time reference to it)? Or is it supposed to be dynamically loaded, itself, in the loadable module (which seems to contradict what I'm seeing in other examples)?
And in either case, how is the GCC command modified to include net-snmp? I've tried variations of whole-archive, no-as-needed, listing what I think is the library (/usr/lib/x86_64-linux-gnu/libsnmp.a) with various compiler options with no effect (or occasionally errors). Also tried linking to the .so version of that (no effect). So a hint as to the proper GCC command to include the library would be very helpful.
Here's one iteration of attempts at linking the main program:
gcc -rdynamic -o TestSO -I../../../include -I/usr/include/libxml2 TestSO.c -ldl -Wl,--no-as-needed /usr/lib/x86_64-linux-gnu/libsnmp.so
I've found numerous examples of loading modules, but they all load a simple routine that does not itself have undefined symbols that need satisfying.
Recap:
TestSO.c
==> Loads with dlopen SNMPmath.c
==> needs to refer to net-snmp routines like init_snmp
Pointers to examples or explanation welcome, I realize I'm missing something fairly obvious.
EDIT AFTER FIRST COMMENTS TO INCLUDE:
I have it sort of working now, but would appreciate a sanity check if this is correct. I pruned it down so as to show the whole code. Here is the code to produce the SO:
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
int zbx_module_SNMPmath_avg(int i, int j)
{
init_snmp("snmpapp"); // initialize SNMP library
return 1;
}
Here is how it is compiled (note slight name change):
CFLAGS=-I. `net-snmp-config --cflags`
SNMPmath_small: SNMPmath_small.c
gcc -shared -o SNMPmath.so $(CFLAGS) -I../../../include -I/usr/include/libxml2 -fPIC SNMPmath_small.c -Wl,--no-as-needed,/usr/lib/x86_64-linux-gnu/libsnmp.so
Then here is the main program:
#include <stdio.h>
#include <dlfcn.h>
#include <dlfcn.h>
#include <string.h>
int main(int argc, char **argv)
{
void *lib_handle;
int(*fn)(int req, int ret);
int x;
char *error;
lib_handle = dlopen("./SNMPmath.so", RTLD_NOW);
if (!lib_handle)
{
fprintf(stderr, "Error on open - %s\n", dlerror());
exit(1);
}
else
fprintf(stderr,"Successfully loaded module\n");
fn = dlsym(lib_handle, "zbx_module_SNMPmath_avg");
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "Error on dlsym %s\n", error);
exit(1);
}
else fprintf(stderr,"Successfully called dlsym\n");
// testing
int req, ret;
req=1;
ret=1;
x=(*fn)(req, ret);
printf("Valx=%i\n",x);
dlclose(lib_handle);
return 0;
}
And finally this is built with:
TestSO: TestSO.c
gcc -rdynamic -o TestSO -I../../../include -I/usr/include/libxml2 TestSO.c -ldl
This now will run as expected. I found that linking against the netsnmp library's so file when building my so seemed to work.
But is this the correct sequence? And/or the preferred sequence?
Ps. Off to read the paper in the first proposed answer.
Pointer to explanation: Drepper's paper: Howto write shared libraries
But we need more source code (and the commands used to compile it), and the real error messages (e.g. as given by dlerror() after dlopen call) to help more.
(See also this answer)
You might want to add some libraries like -lsomething (I don't know which, you probably do know!) to your command gcc -shared which is building SNMPmath.so .... You don't want to link a static library like libsnmp.a to it (you should link shared libraries to your SNMPmath.so shared object).

Trying to understand the main function with GCC and Windows

They say that main() is a function like any other function, but "marked" as an entry point inside the binary, an entry point that the operating system may find (Don't know how) and start the program from there. So, I'm trying to find out more about this function. What have I done? I created a simple .C file with this code inside:
int main(int argc, char **argv) {
return (0);
}
I saved the file, installed the GCC compiler (in Windows, MingW environment) and created a batch file like this:
gcc -c test.c -nostartfiles -nodefaultlibs -nostdlib -nostdinc -o test.o
gcc -o test.exe -nostartfiles -nodefaultlibs -nostdlib -nostdinc -s -O2 test.o
#%comspec%
I did this to obtain a very simplistic compiler and linker, no library, no header, just the compiler. So, the compiling goes well but the linking stops with this error:
test.c:(.text+0xa): undefined reference to '___main'
collect2.exe: error: Id returned 1 exit status
I thought that the main function is exported by the linker but I believed that you didn't need any library with additional information about it. But it looks like it does. In my case I supposed that it must be the standard GCC library, so I downloaded the source code of it and opened this file: libgcc2.c
Now, I don't know if that is the file where the main function is constructed to be linked by GCC. In fact, I don't understand how the main function is used by GCC. Why does the linker need the gcc standard libraries? To know what about main? I hope this has made my question quite specific and clear. Thanks!
When gcc puts together all object files (test.o) and libraries to form a binary it also prepends a small object (usually crt0.o or crt1.o), which is responsible for calling your main(). You can see what gcc is doing, when you add -v on the command line:
$ gcc -v -o test.exe test.o
crt0/crt1 does some setup and then calls into main. But the linker is finally responsible for building the executable according to the OS. With -v you can see also an option for the target system. In my case it's for Linux 64 bit: -m elf_x86_64. For your system this will be something like -m windows or -m mingw.
The error happens because you use these two options: -nodefaultlibs -nostdlib
These tell GCC that it should not link your code against libc.a/c.lib which contains the code which really calls main(). In a nutshell, every OS is slightly different and most of them don't care about C and main(). Each has their own special way to start a process and most of them are not compatible with the C API.
So the solution of the C developers was to put "glue code" into the C standard library libc.a which contains the interface which the OS expects, creates the standard C environment (setting up the memory allocation structures so malloc() will map the OS's memory management functions, set up stdio, etc) and eventually calls main()
For C developers, this means they get a libc.a for their OS (along with the compiler binaries) and they don't need to care about how the setup works.
Another source of confusion is the name of the reference. On most systems, the symbolic name of main() is _main (i.e. one underscore) while __main is the name of an internal function called by the setup code which eventually calls the real main()

Resources