Normally, GDB will load the shared library symbols automatically. You can control this behavior using set auto-solib-add command. However, in some cases (e.g. when debugging with gdbserver and having incompatible symbols or using old Android toolchains) GDB will not load the symbols automatically. In this case you can use the info sharedlibrary command to list the loaded shared libraries and the sharedlibrary command to force the symbols to be loaded.
You just need to call gdb with the executable (it does not matter if it is yours or a 3rd party one). Here is an example where I debug the ls command and set a breakpoint in the (shared) c library. This example uses gdb 6.8 which supports deferred (pending) breakpoints which makes this easy:
how to debug shared library using gdb
Download File: https://garpprepbacksom.blogspot.com/?wf=2vAN7e
Normally the procedure for debugging a shared library is much the same as for debugging an executable - the main difference is that you may be unable to set a breakpoint until the shared library is loaded into memory. You attach the debugger to the main executable.
If you are debugging an application that is not owned by you, but is using your module in a plugin architecture, you still use the same method. Make sure (as always) that you have debugging information available for your shared library. In windows, you would generate a .pdb file. With gcc, I think you specify a special compiler flag (-g?) to ensure that debugging information is supplied. You attach the debugger to the third party application.
I'm running tests on a dynamic library test.so (compiled from test.c) in Linux using python and python's unit-testing library unittest called tests/test_pwmbasic.py. (naming scheme is a bit monotone, I realise that now)
I remember testing shared libraries by creating a mock app that used it. If you are willing to do a lot of work, you could create a second, mock shared library that just collects information about how the library is being used by the third party app, and then have your mock app replay that information.
When using gdb, I expect "s" (step) to step into the 3rd party library and show me the lines it is executing inside these opj_* functions instead of just going to the next line in my own shared library code.
Use filename as the program to be debugged. It is read for itssymbols and for the contents of pure memory. It is also the programexecuted when you use the run command. If you do not specify adirectory and the file is not found in the GDB working directory,GDB uses the environment variable PATH as a list ofdirectories to search, just as the shell does when looking for a programto run. You can change the value of this variable, for both GDBand your program, using the path command.
When GDB is configured for a particular environment, itunderstands debugging information in whatever format is the standardgenerated for that environment; you may use either a GNU compiler, orother compilers that adhere to the local conventions.Best results are usually obtained from GNU compilers; for example,using GCC you can generate debugging information foroptimized code.
Although filename is typically a shared library file, anexecutable file, or some other object file which has been fullyrelocated for loading into a process, you can also load symbolicinformation from relocatable .o files, as long as:
If mode is on, symbols from all shared object librarieswill be loaded automatically when the inferior begins execution, youattach to an independently started inferior, or when the dynamic linkerinforms GDB that a new library has been loaded. If modeis off, symbols must be loaded manually, using thesharedlibrary command. The default value is on.
If your program uses lots of shared libraries with debug info thattakes large amounts of memory, you can decrease the GDBmemory footprint by preventing it from automatically loading thesymbols from shared libraries. To that end, type setauto-solib-add off before running the inferior, then load eachlibrary whose debug symbols you do need with sharedlibraryregexp, where regexp is a regular expression that matchesthe libraries whose symbols you want to be loaded.
Load shared object library symbols for files matching aUnix regular expression.As with files loaded automatically, it only loads shared librariesrequired by your program for a core file or after typing run. Ifregex is omitted all shared libraries required by your program areloaded.
Unload all shared object library symbols. This discards all symbolsthat have been loaded from all shared libraries. Symbols from sharedlibraries that were loaded by explicit user requests are notdiscarded.
This command controls whether GDB should give you controlwhen the dynamic linker notifies it about some shared library event.The most common event of interest is loading or unloading of a newshared library.
This article explains how you can use gdb to debug a program with the core file, how to display assembly language instructions of your program, and how to load shared library programs for debugging.
The focus of the information on this page is to communicate GStreamer shared library debugging, not describing how to build GStreamer or how to use GDB. For that reason, there is a large cut-and-paste set of commands that are not well explained except for the parts that are of interest to GStreamer shared library debugging.
The debugging session is done on a Ubuntu host. The inspiration of the Ubuntu debugging session was to watch a buffer get passed down the GStreamer pipeline. To have GStreamer shared libraries that are built with debug symbols, the libraries had to be rebuilt.
When we build and install the debug versions of the GStreamer library, we don't want to have that library affecting any other Ubuntu applications, like VLC. To avoid polluting the Ubuntu workstation, we install the custom build in a locale directory using the --prefix configure parameter and then we have to tell the GStreamer application where to find the shared GStreamer libraries from the custom build using the GST_PLUGIN_SYSTEM_PATH shell environment variable.
You can't set a breakpoint in a shared library unless the library is already loaded. To address this issue, the LD_PRELOAD environment variable is set to the list of shared libraries we want pre-loaded for debugging.
You can't list the shared libraries to preload unless you can identify them. A simple way to do that is using 'strace and grabbing just of system calls of interest - namely open() calls.
Even if I answer yes, the breakpoint is never reached, even though the function is called on execution. Also, if I attempt to step through the statements in foo.c, it steps over the call into the shared library.
The gdb 6.3 from the HP WDB package does work. Functions from shared libraries are not immediately recognizable (.i.e., I still get "Breakpoint deferred until a shared library containing "hello" is loaded."), but that's only a minor annoyance as program execution actually does break when it hits that function.
Performing dynamic analysis on embedded system firmware can greatly improve the speed of the reverse engineering process. Disassemblers are essential for static analysis, but can also be used during dynamic analysis. While debugging during dynamic analysis is usually pretty straight forward in most disassemblers, they don't seem to offer an obvious way to follow the program flow when the application you are working on jumps to a shared library. Being able to follow that shared library jump in the disassembler allows you to better understand the program flow, and could help with patching the shared library if needed. There are a few issues that need to be solved before this is possible. First the memory location of the shared library will be unknown until the application or process has been started, and second the disassembler will treat the application and shared library as separate instances. So in order to use the disassembler for debugging while running dynamic analysis we will need a way to work around these issues. Below, I will introduce a simple technique for performing dynamic analysis of shared libraries using gdb, Binary Ninja, Voltron, and binjatron a plugin for Binary Ninja that allows for visual debugging during dynamic analysis.
The example program is straight forward. A simple shared library compiled with '-fpic -c' and '-shared'. A main program which calls a function in the shared library which is then compiled with the location and name of the library. Below shows the results and source. After compiling, LD_LIBRARY_PATH is set and the program is run. I also run 'ldd' to verify that the program is expecting libfoo as a shared lib.
Pulling these programs into Binary Ninja we can verify things look correct. Below we can see the main program, and the foo shared library loaded into binary ninja using medium IL view on the libfoo.so. Everything looks correct so now I can start the gdbserver on the target using 'gdbserver host:2345 main. To connect to session you will need a gdb capable of performing cross architecture debugging. I have used 'arm-linux-gnueabihf-gdb' in the past and it seems to work here. Inside of gdb use 'target remote target-ip:2345' to make the connection.
Now that I have verified everything is working nicely together, I can begin the setup process and perform dynamic analysis on the shared library. As stated earlier, if I simply continued stepping the application, once main calls foo() I will lose the current instruction highlighting due to the shared library code not being in the application's binary. Gdb and voltron alone could be used to perform dynamic analysis, but in larger functions with switch tables, having a graph view can give you a better understanding of the flow. You could also use the gdb/voltron output to manually follow along with the disassembly in Binary Ninja, but making it all work together in one view can have it's advantages by speeding up the process. The biggest issue here is getting the shared library offset synced in Binary Ninja with where the application expects the code. The voltron link will not sync since the address is offset. The simple solution is to offset the loading address of the shared library and resync voltron and that is what we are going to do. First the file offset needs to be obtained somehow. While there can be many ways to do this, in this case I simply place a breakpoint at foo(). Your approach could be different. Below we can see this breakpoint gives us an address of 0xb6fa6620. Analyzing the library in Binary Ninja we see the 0x620 offset is in foo() after the stack has been saved going into the function call. We can now use this information to calculate where we should offset the starting address, 0xb6fa6620 - 0x620 = 0xb6fa6000. While Binary Ninja has no obvious way to rebase the binary using the user interface, it does have an api call that seems to do the trick. While they don't give an example or description of the call, add_auto_segment seems to copy data from the current selected file, and give it an offset. In this instance, for add_auto_segment(self, start, length, data_offset, data_length, flags): start will be our offset address of 0xb6fa6000, length will be the original file length, data_offset will be 0, and data_length will be the original file length. I also pass in 0xff for flags to make the segment writable, readable, and executable. The final command will be 'bv.add_auto_segment(0xb6fa6000, 0x6a0, 0, 0x6a0, 0xFF)'. Below you can see the results of running the command in Binary Ninja's python console. 2ff7e9595c
Kommentit