Debugging Shared Libraries With GDB-Embedded Linux Online Course

PREV_LEC | NEXT_LEC

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

shared library debugging libc-dbg BR2_ENABLE_DEBUG show directories free embedded systems course

What You Will Learn

  • How the Yocto Project packages and installs debug variants of shared libraries
  • How Buildroot’s BR2_ENABLE_DEBUG option 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

AspectYocto ProjectBuildroot
Enable mechanismPer-package -dbg or dbg-pkgs featureSingle global BR2_ENABLE_DEBUG
Where symbols live.debug subdirs inside the deployed imageToolchain staging sysroot only
Target image impactCan grow by hundreds of MB if dbg-pkgs used broadlyNone by default — target copies stay stripped
Source code shipped?Yes by default, disable via PACKAGE_DEBUG_SPLIT_STYLENot 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-pkgs for every package on a small flash target — usually unnecessary; pull in specific -dbg packages only for libraries you’re actively investigating.
  • Forgetting Buildroot’s target copies stay stripped — BR2_ENABLE_DEBUG alone 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 directory to 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 directory rather than recompiling just to fix path metadata.

Summary and Key Takeaways

  • Yocto packages per-library debug variants (-dbg) that install into a hidden .debug directory inside the image.
  • Buildroot’s BR2_ENABLE_DEBUG builds all libraries with debug symbols into the staging sysroot, leaving target copies stripped unless you disable target stripping too.
  • show directories and directory <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
PREV_LEC | NEXT_LEC

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *