Manual Symbolication with LLDB
LLDB is separated into a shared library that contains the core of the debugger, and a driver that implements debugging and a command interpreter. LLDB can be used to symbolicate your crash logs and can often provide more information than other symbolication programs:
- Inlined functions
- Variables that are in scope for an address, along with their locations
The simplest form of symbolication is to load an executable:
(lldb) target create --no-dependents --arch x86_64 /tmp/a.out
We use the "--no-dependents" flag with the "target create" command so that we don't load all of the dependent shared libraries from the current system. When we symbolicate, we are often symbolicating a binary that was running on another system, and even though the main executable might reference shared libraries in "/usr/lib", we often don't want to load the versions on the current computer.
Using the "image list" command will show us a list of all shared libraries associated with the current target. As expected, we currently only have a single binary:
(lldb) image list
[ 0] 73431214-6B76-3489-9557-5075F03E36B4 0x0000000100000000 /tmp/a.out
/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out
Now we can look up an address:
(lldb) image lookup --address 0x100000aa3
Address: a.out[0x0000000100000aa3] (a.out.__TEXT.__text + 131)
Summary: a.out`main + 67 at main.c:13
Since we haven't specified a slide or any load addresses for individual sections in the binary, the address that we use here is a file address. A file address refers to a virtual address as defined by each object file.
If we didn't use the "--no-dependents" option with "target create", we would have loaded all dependent shared libraries:
(lldb) image list
[ 0] 73431214-6B76-3489-9557-5075F03E36B4 0x0000000100000000 /tmp/a.out
/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out
[ 1] 8CBCF9B9-EBB7-365E-A3FF-2F3850763C6B 0x0000000000000000 /usr/lib/system/libsystem_c.dylib
[ 2] 62AA0B84-188A-348B-8F9E-3E2DB08DB93C 0x0000000000000000 /usr/lib/system/libsystem_dnssd.dylib
[ 3] C0535565-35D1-31A7-A744-63D9F10F12A4 0x0000000000000000 /usr/lib/system/libsystem_kernel.dylib
...
Now if we do a lookup using a file address, this can result in multiple matches since most shared libraries have a virtual address space that starts at zero:
(lldb) image lookup -a 0x1000
Address: a.out[0x0000000000001000] (a.out.__PAGEZERO + 4096)
Address: libsystem_c.dylib[0x0000000000001000] (libsystem_c.dylib.__TEXT.__text + 928)
Summary: libsystem_c.dylib`mcount + 9
Address: libsystem_dnssd.dylib[0x0000000000001000] (libsystem_dnssd.dylib.__TEXT.__text + 456)
Summary: libsystem_dnssd.dylib`ConvertHeaderBytes + 38
Address: libsystem_kernel.dylib[0x0000000000001000] (libsystem_kernel.dylib.__TEXT.__text + 1116)
Summary: libsystem_kernel.dylib`clock_get_time + 102
...
To avoid getting multiple file address matches, you can specify the name of the shared library to limit the search:
(lldb) image lookup -a 0x1000 a.out
Address: a.out[0x0000000000001000] (a.out.__PAGEZERO + 4096)