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.
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:
listobj
command
listobj
command
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:
listobj
command
listobj
command
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.
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.
(ladebug) file func.c (ladebug) list 10 10 11 int funcD(i) { 12 13 int a; > 14 a = i; 15 return a; 16 }
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.
(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)
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.
(ladebug) print a 1 (ladebug) which a "func.c"`funcD.a (ladebug) assign a = 2 (ladebug) print a 2 (ladebug)
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.
(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)
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.
(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.
(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.
(ladebug) list 1:5 1 2 #include <stdio.h> 3 4 int gfi = 100; 5 (ladebug) print strlen("abc") 3 (ladebug)
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.
(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)
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.
(ladebug) 0x3ffbf800b98 /2i *[funcC:6, 0x3ffbf800b98] addq zero, 0x64, t0 [funcC:6, 0x3ffbf800b9c] stl t0, 8(sp) (ladebug)