Debugging Shared Libraries With GDB
Getting debug symbols and source code for libc and friends — free linux device drivers course
Your own application’s debug symbols are only half the picture — the moment execution enters libc, libpthread, or any other shared library, GDB needs debug information for those too, or your backtraces dead-end at opaque addresses. This lecture in our free linux kernel development course covers how Yocto and Buildroot each expose library debug symbols, and how to point GDB at moved or unusual source locations.
Keywords
What You Will Learn
- How the Yocto Project packages and installs debug variants of shared libraries
- How Buildroot’s
BR2_ENABLE_DEBUGoption builds line-level debug symbols - How GDB’s source search path works, and how to add custom directories to it
- How to keep target image size under control while still debugging effectively on the host
Prerequisites
- Completed the sysroot setup from the earlier lecture in this series
- A Yocto or Buildroot build environment you can rebuild packages in
Debug Symbols for Libraries in Yocto
The Yocto Project builds separate debug variants of every binary package and places them under build/tmp/deploy/<package-manager>/<target-architecture>/. For the C library, for example, you’d see a package like this:
build/tmp/deploy/rpm/aarch64/glibc-dbg-2.39-r0.aarch64.rpm
You can pull specific debug packages into your target image selectively, by appending -dbg to the package name in your image recipe. For glibc that means adding glibc-dbg. Alternatively, tell Yocto to install every available debug package at once via EXTRA_IMAGE_FEATURES:
EXTRA_IMAGE_FEATURES = "dbg-pkgs"
Be deliberate about this — installing every debug package can inflate a target image by several hundred megabytes, which matters a great deal on flash-constrained boards. Yocto places these symbols in a hidden .debug subdirectory inside both lib/ and usr/lib/, and GDB already knows to look there once your sysroot points at the right unpacked image.
The debug packages also carry a full copy of source code, installed under usr/src/debug/<package-name> — a large chunk of that size increase. If you only debug remotely (source and symbols live on the host, not the target), you can skip shipping the source entirely by adding this to your recipe:
PACKAGE_DEBUG_SPLIT_STYLE = "debug-without-src"
And since remote debugging only ever needs symbols and source on the host, nothing stops you from deleting lib/.debug, usr/lib/.debug, and usr/src from the copy of the image that actually gets flashed onto the target.
Debug Symbols for Libraries in Buildroot
Buildroot’s approach is more straightforward: a single global option rebuilds everything with line-level debug symbols.
BR2_ENABLE_DEBUG— under Build options → build packages with debugging symbols
This produces libraries with debug symbols in output/host/usr/<arch>/sysroot — the same staging sysroot you already pointed GDB at in an earlier lecture — while the copies that go into the actual target image remain stripped by default. If you specifically need debug symbols present on the target too (for example, to run GDB natively rather than remotely), disable target stripping:
- Build options → strip command for binaries on target → set to
none
Yocto vs Buildroot: Library Debug Symbols
| Aspect | Yocto Project | Buildroot |
|---|---|---|
| Enable mechanism | Per-package -dbg or dbg-pkgs feature | Single global BR2_ENABLE_DEBUG |
| Where symbols live | .debug subdirs inside the deployed image | Toolchain staging sysroot only |
| Target image impact | Can grow by hundreds of MB if dbg-pkgs used broadly | None by default — target copies stay stripped |
| Source code shipped? | Yes by default, disable via PACKAGE_DEBUG_SPLIT_STYLE | Not shipped to target either way |
Finding Source Code GDB Can’t Locate
Beyond libraries with dedicated debug packages, GDB also needs to know where your own or third-party source lives if it has moved since compilation. Check the current search path with show directories:
(gdb) show directories
Source directories searched: $cdir:$cwd
By default GDB searches $cdir (the directory the source was compiled in, recorded in the debug info) and $cwd (GDB’s current working directory). That’s normally sufficient — but if the source tree has since been relocated, add the new path explicitly with directory:
(gdb) directory /home/dev/projects/ep_sensor/src/lib_calibration
Source directories searched:
/home/dev/projects/ep_sensor/src/lib_calibration:$cdir:$cwd
New directories are prepended and searched first, which is useful when you have multiple copies of similarly named source floating around and want to be sure GDB picks the right one.
Real-World Use Case
Picture a crash deep inside pthread_mutex_lock() during stress testing. Without a matching debug package for libpthread/glibc, GDB’s backtrace stops at an opaque address inside the library — you can see you’re stuck on a mutex, but not why. Installing the matching -dbg package (Yocto) or enabling BR2_ENABLE_DEBUG and rebuilding (Buildroot) turns that opaque frame into a real source line, often revealing the actual culprit is a double-lock or missing unlock several frames up in your own code — not libc misbehaving at all.
Common Mistakes and Troubleshooting
- Enabling
dbg-pkgsfor every package on a small flash target — usually unnecessary; pull in specific-dbgpackages only for libraries you’re actively investigating. - Forgetting Buildroot’s target copies stay stripped —
BR2_ENABLE_DEBUGalone doesn’t put symbols on the target; that’s controlled separately via the strip-on-target setting. - Assuming source auto-follows a moved project directory — GDB records the original compile-time path; use
directoryto add the new location.
Best Practices
- Keep library debug symbols on the host sysroot only — avoid shipping them to production targets.
- Use
PACKAGE_DEBUG_SPLIT_STYLE = "debug-without-src"in Yocto if you don’t need on-target source, to save significant image size. - Add moved source directories with
directoryrather than recompiling just to fix path metadata.
Summary and Key Takeaways
- Yocto packages per-library debug variants (
-dbg) that install into a hidden.debugdirectory inside the image. - Buildroot’s
BR2_ENABLE_DEBUGbuilds all libraries with debug symbols into the staging sysroot, leaving target copies stripped unless you disable target stripping too. show directoriesanddirectory <path>manage GDB’s source search path when files have moved.
Conclusion
Full visibility into library internals turns “stuck inside libc, no idea why” into an actual, actionable stack trace. With application, library, and source debugging all covered, the final lecture in this GDB chapter of our free linux development course tackles attaching to already-running processes, debugging forked children, multithreaded programs, and getting your first look at core files.
FAQ
Do I need library debug symbols on the target device itself?
No, for remote debugging with gdbserver — GDB only needs them on the host, where sysroot points. Target-side symbols are only needed for native on-device debugging.
How much can dbg-pkgs increase my Yocto image size?
Potentially several hundred megabytes, since it installs debug variants and source for every package. Prefer targeted per-package -dbg additions instead.
Does BR2_ENABLE_DEBUG automatically put debug symbols on the target image?
No — target binaries remain stripped by default even with BR2_ENABLE_DEBUG on. You must separately set the strip-on-target option to none if you need symbols on-device.
What does the directory command actually change?
It prepends a path to GDB’s source search list for the current session, so source files GDB can’t find at their originally recorded path get located there instead.
Is PACKAGE_DEBUG_SPLIT_STYLE = debug-without-src safe to use?
Yes for remote debugging workflows — it just stops Yocto from copying source code into the target image, which you don’t need there since source lives on your host.
Continue the Free Embedded Systems Course
Next: attaching to running processes, and debugging forks, threads, and core files.
Browse All Lectures Join EmbeddedPathashala
2 Comments