If a process manually loads a shared library through dlopen(), GDB may sometimes fail to recognize its debugging symbols. In that case, you can use GDB’s add-symbol-file file address command to add the symbols manually. address is the entry address of the library’s .text segment in the process. Since the real address is only known after the library has been loaded into the process, we can use GDB’s info files command to obtain the entry address of that library’s .text segment in the process.

The procedure is as follows:

First, disassemble the target shared library so we can use it later as a comparison reference:

objdump -d demo.so > demo.s

Then use gdb to debug the target process:

 1(gdb) info files # Get the entry address of the library's .text segment
 2...
 30x00007f1fc9f3ba50 - 0x00007f1fca549490 is .text in /path/demo.so
 4...
 5(gdb) add-symbol-file /path/demo.so 0x00007f1fc9f3ba50 # Load the library's debug info (the library here was built with debug symbols)
 6add symbol table from file "demo.so" at
 7                .text_addr = 0x7f1fc9f3ba50
 8(y or n) u
 9Reading symbols from demo.sp...done.
10(gdb) disassemble test_function # Inspect any function in the library for comparison
11Dump of assembler code for funtion test_function:
120x...77adf0<+0>:     sub     $0x218.%rsp
130x...77adf7<+7>:     mov    %fs:0x28,%rax
14...

Open the demo.s file you just disassembled with any editor and search for this function:

000000077adf0 <test_function>:
77adf0:   ...  sub     $0x218.%rsp
77adf7:   ...  mov    %fs:0x28,%rax

You can see that the address offsets and instructions in GDB’s disassembly match the objdump output. You can also set a breakpoint and verify that execution stops at the correct location.