16 Debugging Shared Libraries

You can debug shared libraries if the library was compiled and linked with the option that makes symbol table information available to the debugger. For more information about shared libraries and linking programs to shared libraries, and compiling and linking programs and libraries for debugging, see your compiler documentation.

Loadable drivers can be considered a form of shared libraries, and can be debugged. For information, see Chapter 22 on kernel debugging.

You can debug shared libraries using the same techniques you use to debug any program function. The scope and visibility rules for debugging programs apply to debugging shared libraries.

You can call functions in shared libraries that do not have debugging information available.

16.1 Controlling the Reading of Symbols for Shared Libraries

In general, build your images with symbol tables. You cannot set symbolic breakpoints to stop execution in or see the source code of shared libraries that do not have symbol table information available for the debugger.

By default, symbol table information is read into the debugger for all the shared libraries that are loaded, whether loaded at startup of the application or loaded dynamically by the application at run time.

For less bulky images, you can strip out symbol table information with the UNIX command ostrip (for information, see the ostrip(1) reference page). You can also use debugger commands to control symbols, as follows:

-nosharedobjs

The -nosharedobjs flag, used on the ladebug command, directs the debugger not to read symbol table information for any of the shared objects. (Later, you can use the the readsharedobj command at the (ladebug) prompt to read in the symbol table information for a specified shared object.)

listobj

The listobj command lists all the objects (the main image and all shared libraries) that are currently used by the debuggee process. For each object, it lists the full object name (with pathname), the starting address for the text, the size of the text region, and whether the symbol table information for this object is read by the debugger.

The pathnames listed are the ones actually used by the run-time loader when loading the shared libraries.

readsharedobj   objectname

The readsharedobj command directs the debugger to read in the symbol table information for objectname, which must be a shared library. The command can only be used when a debuggee program is specified (that is, either Ladebug was invoked with it or the debuggee was loaded by the load command).

The symbol table information of objectname will be read into the debugger, provided that objectname is specified as either of the following:

If no match is found, no symbol table information is read into the debugger. If no unique match (for a simple name) is found, no symbol table information is read, and an error message is issued. If the symbols for the specified object have already been read, no symbol table information is read.

The readsharedobj command currently only reads in the symbol table information of a shared object that has already been dynamically loaded when the program executes.

delsharedobj   objectname

The delsharedobj command directs the debugger to remove the symbols for objectname, which must be a shared object.

If objectname is a complete file specification, for example, /usr/shlib/libc.so, then the specified name is used as provided.

The symbol table information of objectname will be removed from the debugger, provided that objectname is specified as either of the following:

If no match is found, no symbol table information is removed from the debugger. If no unique match (for a simple name) is found, no symbol table information is removed, and an error message is issued.

If the last modification time and/or size of the binary file or any of the shared objects used by the binary file has changed since the last run or rerun command was issued, Ladebug automatically rereads the symbol table information when you execute the program again.

16.2 Listing the Shared Library Source Code

Provided your library was compiled with -g , to list the source code for the shared library, set the file context to the file containing the source and enter the list command. See Example 16-1.

Example 16-1 Listing the Shared Library Source Code

(ladebug) file func.c
(ladebug) list 10
     10
     11 int funcD(i) {
     12
     13     int a;
>    14     a = i;
     15     return a;
     16 }

16.3 Setting Breakpoints in a Shared Library

Provided your library was compiled with -g , set breakpoints or tracepoints in a shared library by setting a breakpoint on a specific line of source code or function contained in the library, as shown in Example 16-2.

Example 16-2 Setting Breakpoints in a Shared Library

(ladebug) stop in main;r
[#1: stop in main ]
[1] stopped at [main:28 0x120001280]
     28   a = 1;

(ladebug) stop in funcC
[#2: stop in funcC ]

(ladebug) file func.c

(ladebug) stop at 14
[#3: stop at "func.c":14 ]

(ladebug) status
#1 PC==0x120001280 in main "file.c":28 { break }
#2 PC==0x3ffbf800b98 in funcC "func.c":6 { break }
#3 PC==0x3ffbf800bd8 in funcD "func.c":14 { break }

(ladebug) c
having fun yet?!
[2] stopped at [funcC:6 0x3ffbf800b98]
      6     a = m = n = 100;

(ladebug) c
[3] stopped at [funcD:14 0x3ffbf800bd8]
     14     a = i;
(ladebug)

16.4 Printing and Modifying Shared Library Variable Values

Provided your library was compiled with -g , you can access variables in shared libraries if the variable is visible and in scope according to the rules of the program language. Print and modify the variable the same way you print or modify any program variable, as shown in Example 16-3.

Example 16-3 Printing and Modifying Shared Library Variable Values

(ladebug) print a
1

(ladebug) which a
"func.c"`funcD.a

(ladebug) assign a = 2

(ladebug) print a
2
(ladebug)

16.5 Stepping into Shared Library Functions

When you are executing the program using the step command, the debugger will step into a function in the shared library the same way the debugger steps into any other program function, as shown in Example 16-4.

Example 16-4 Stepping into Shared Library Functions

(ladebug) list
     29   b = 2;
     30   c = 3;
     31
     32   printf ("having fun yet?!\en");
     33   printf ("%d  %d %d %d\en", funcA(a), funcB(a), funcC(a), funcD(a));
     34   printf ("NOT\en");
     35 }
     36
     37

(ladebug) step
stopped at [main:32 0x120001298]
     32   printf ("having fun yet?!\en");

(ladebug) step
having fun yet?!
stopped at [main:33 0x1200012b0]
     33   printf ("%d  %d %d %d\en", funcA(a), funcB(a), funcC(a), funcD(a));

(ladebug) step
stopped at [funcA:11 0x120001200]
     11     x = 1;
(ladebug) step
stopped at [funcA:12 0x120001208]
     12     a = i + g1 + x;
(ladebug)

16.6 Calling a Shared Library

Call a shared library function explicitly using the call command. You can also embed a call to a function contained in a shared library as shown in Example 16-5.

Example 16-5 Calling a Shared Library

(ladebug) call funcB(2)

(ladebug) print funcB(2)
2
(ladebug)

You can also nest calls to functions in shared libraries as shown in Example 16-6.

Example 16-6 Nesting Calls to Shared Libraries

(ladebug) rerun
[1] stopped at [main:28 0x120001280]
     28   a = 1;

(ladebug) call funcD(3)
[3] stopped at [funcD:14 0x3ffbf800bd8]
     14     a = i;

(ladebug) where
>0  0x3ffbf800bd8 in funcD(i=3) func.c:14
#1  0x1200011d0 in _mcount()

(ladebug) print funcB(3)
3
(ladebug)

Although it is very time-consuming and tedious to debug shared libraries that do not have symbolic information available for the debugger, you can call functions in the shared libraries. Example 16-7 shows, for example, that if your program links the stdio shared library, you can call the strlen function.

Example 16-7 Calling a System Library Function

(ladebug) list 1:5
      1
      2 #include <stdio.h>
      3
      4 int gfi = 100;
      5

(ladebug) print strlen("abc")
3
(ladebug)

16.7 Accessing Shared Libraries on the Stack Trace

If a shared library is active on the stack trace when you enter the where command, the shared library will be displayed. You can use the up, down, and func commands to change the func context, as shown in Example 16-8.

Example 16-8 Accessing Shared Libraries on the Stack Trace

(ladebug) where
>0  0x3ffbf800b98 in funcC(i=1) func.c:6
#1  0x1200012ec in main() file.c:33

(ladebug) up
>0  0x1200012ec in main() file.c:33
     33   printf ("%d  %d %d %d\en", funcA(a), funcB(a), funcC(a), funcD(a));

(ladebug) where
#0  0x3ffbf800b98 in funcC(i=1) func.c:6
>1  0x1200012ec in main() file.c:33

(ladebug) func funcC
funcC in func.c line No. 6:
      6     a = m = n = 100;

(ladebug) where
>0  0x3ffbf800b98 in funcC(i=1) func.c:6
#1  0x1200012ec in main() file.c:33
(ladebug)

16.8 Disassembling a Memory Address in a Shared Library

Disassemble and modify shared library values contained in memory the same way you disassemble other program function values. In Example 16-9, a range of addresses is disassembled.

Example 16-9 Disassembling a Memory Address in a Shared Library

(ladebug) 0x3ffbf800b98 /2i
*[funcC:6, 0x3ffbf800b98]       addq    zero, 0x64, t0

 [funcC:6, 0x3ffbf800b9c]       stl     t0, 8(sp)

(ladebug)