Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Why your errno value isn't printing in GDB—and what to do about it

June 5, 2024
Kevin Buettner
Related topics:
C, C#, C++CompilersLinux
Related products:
Red Hat Enterprise Linux

    When debugging a program using the GNU Project Debugger (GDB), printing the value of errno should be easy, but sometimes it isn't. This article explains why printing errno might not work and what to do when it doesn't.

    This article will focus on systems with runtime environments which use the GNU C Library (glibc) as the C library; printing errno on other modern multi-threaded systems which use a different C library implementation should be similar, though some of the details for accessing errno (when it doesn't just work) might be different. This article with be most applicable to GNU/Linux systems running either the Red Hat Enterprise Linux (RHEL) or Fedora distributions. Much of the article should be applicable to other popular Linux distributions too, but be aware that package names may be different and that commands used for installing packages may also be different than those shown here for RHEL and Fedora.

    About errno

    The C and C++ programming languages provide access to, via the C library, a variable (or, more pedantically, a modifiable lvalue) named errno. Historically, before multi-threaded environments became commonplace, errno was a global variable of type int, but for most modern systems, it's a per-thread lvalue; i.e., storage associated with errno will need to reside in thread-local storage. The type of errno is still the same though—it's still int, just as it was in the past.

    Code written in C or C++ can examine errno after calling certain library functions when the return value from the call indicates that an error occurred. Values associated with errno are positive integers which indicate the kind of error that occurred. For example, error code 2 or ENOENT indicates "No such file or directory". On Fedora systems, if the moreutils package is installed, a list of errno values may be obtained by running errno -l from the shell.

    Example program

    Consider this example code, named open-error.c, written in C:

    #include <stdio.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <string.h>
    
    int
    main (int argc, char **argv)
    {
      int fd = open ("/path/to/a/file/which/does/not/exist", O_RDONLY);
    
      if (fd < 0)			/* Line 11: Set GDB breakpoint here.  */
        printf ("open returned error: %s (%d)\n", strerror (errno), errno);
    }

    This code may be compiled using the following command:

    gcc -Wall -g -o open-error ./open-error.c

    Running it, along with its output, looks like this:

    $ ./open-error
    open returned error: No such file or directory (2)

    The program attempts to open a nonexistent file. Since the file does not exist, the value returned by open is negative, causing the error message to be printed. Had the file existed, running the program would not have printed anything—it would have been opened via the call to open and then closed during program exit.

    Printing errno with GDB

    Printing errno from within GDB should be easy. To demonstrate the ideal case, where it just works, let's debug this program with GDB and set a breakpoint on line 11, which is the first executable line after the call to open:

    $ gdb -q open-error
    Reading symbols from open-error...
    (gdb) break 11
    Breakpoint 1 at 0x40117d: file ./open-error.c, line 11.
    (gdb) 

    Let's run the program, answering y when asked whether to enable debuginfod:

    (gdb) run
    Starting program: /tmp/examp/open-error
    This GDB supports auto-downloading debuginfo from the following URLs:
     <https://debuginfod.fedoraproject.org/>
    Enable debuginfod for this session? (y or [n]) y
    Debuginfod has been enabled.
    To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
    Downloading separate debug info for /lib64/libc.so.6                            
    [Thread debugging using libthread_db enabled]                                   
    Using host libthread_db library "/lib64/libthread_db.so.1".
    Breakpoint 1, main (argc=1, argv=0x7fffffffdb88) at ./open-error.c:11
    11      if (fd < 0)            /* Line 11: Set GDB breakpoint here.  */

    Finally, let's print fd and errno:

    (gdb) print fd
    $1 = -1
    (gdb) print errno
    $2 = 2

    This, ideally, is how things should work. But there are cases where printing errno might not work. The rest of the article will examine cases where it does not work and what, if anything, can be done to still discover the value of errno using GDB.

    Missing glibc debugging information (debuginfo)

    One possibility is that the system does not have the glibc-debuginfo package installed and debuginfod is either not used or is not available. When GDB doesn't have access to glibc debugging information, it won't have access to type information for errno and __errno_location, which is the function that, when called, returns the address of errno storage for the current thread.

    I'm running this example on rawhide (Fedora 41) in which the system GDB no longer contains a hack which would more frequently allow errno to be printed. Also, on this machine, I've removed the glibc-debuginfo package.

    Below, I run the program again, but this time, I'll answer n when asked whether to enable debuginfod. I do this only for demonstration purposes—in practice, you should ensure that glibc debugging information is available from some source, either from installed glibc-debuginfo or via using debuginfod, in which debugging information is obtained on-demand.

    $ gdb -q open-error
    Reading symbols from open-error...
    (gdb) b 11
    Breakpoint 1 at 0x40117d: file ./open-error.c, line 11.
    (gdb) run
    Starting program: /tmp/examp/open-error
    This GDB supports auto-downloading debuginfo from the following URLs:
     <https://debuginfod.fedoraproject.org/>
    Enable debuginfod for this session? (y or [n]) n
    Debuginfod has been disabled.
    To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib64/libthread_db.so.1".
    Breakpoint 1, main (argc=1, argv=0x7fffffffdb88) at ./open-error.c:11
    11      if (fd < 0)            /* Line 11: Set GDB breakpoint here.  */
    Missing debuginfo, try: dnf debuginfo-install glibc-2.39.9000-18.fc41.x86_64

    Note that GDB has detected the fact that it's missing debugging information—it even provides a suggestion for installing it. But, for the time being, let's ignore that suggestion and attempt to print errno:

    (gdb) print errno
    'errno' has unknown type; cast it to its declared type

    Let's follow the suggestion and add a cast:

    (gdb) print (int) errno
    $1 = 2

    So, for this case, when missing glibc debugging information, which provides the type of errno, we can simply add a cast to print it out.

    Another (better) way is to make sure that glibc-debuginfo is installed. On Fedora, this can be done from the shell, like this:

    $ sudo debuginfo-install glibc
    [sudo] password for ...:
    ...
    Installed:
      glibc-debuginfo-2.39.9000-18.fc41.i686                                        
      glibc-debuginfo-2.39.9000-18.fc41.x86_64                                      
    
    Complete!
    

    Once that is done, and GDB is restarted, and run using commands shown earlier, it'll be possible to print errno without the cast:

    (gdb) p errno
    $1 = 2

    Another way to cause GDB to load and use glibc debugging information is to enable debuginfod. This can be done by answering y to the question "Enable debuginfod for this session (y or [n])". This will work even when the glibc-debuginfo package is not installed on the system; GDB's debuginfod support will cause any needed debugging information to be downloaded from a debuginfod server.

    Problems with finding thread-local storage

    On modern systems, errno is located in thread-local storage. This needs to be the case because if two threads both make a system call at roughly the same time, these threads need access to errno without it being clobbered by the other thread. On GNU/Linux systems, errno is placed in thread-local storage even in programs which don't use threads.

    At the time that this article was written, on GNU/Linux, GDB finds addresses in thread-local storage by using a helper library named libthread_db.so. If this library isn't available or if the program wasn't linked against a library containing libpthread functionality, GDB won't be able to find thread-local storage, including errno.

    So why does GDB work for the example above? It's not multi-threaded and the compile line didn't include -lpthread. Well, it turns out that ever since glibc-2.34, the bulk of libpthread's functionality has been moved into libc.so. This means that GDB is still able to use the helper library libthread_db.so to find thread-local storage.

    But, when using an older system, or even a current system using versions of glibc older than 2.34, problems with accessing errno can arise. If the example program is built and run on Fedora 34 (which uses glibc-2.33), using a GDB built from upstream sources, attempting to print errno will show:

    (gdb) print errno
    Cannot find thread-local storage for process 87818, shared library /lib64/libc.so.6:
    Cannot find thread-local variables on this target

    If you use the system GDB on Fedora 34 (or any other Fedora release before 41), this example will work as expected. The reason for this is that those versions of GDB were hacked to intercept errno and rewrite it as *(*(int *(*)(void)) __errno_location) (). Here, __errno_location is an internal function that returns the address of errno for the current thread.

    So, this provides us with a way to print errno when there are problems with finding thread-local storage:

    (gdb) print *(*(int *(*)(void)) __errno_location) ()
    $1 = 2

    That expression is cumbersome to type, but GDB's macro define command may be used to make things easier:

    (gdb) macro define errno *(*(int *(*)(void)) __errno_location) ()
    (gdb) print errno
    $3 = 2

    Note that the macro define errno *(*(int *(*)(void)) __errno_location) () command can be placed in a .gdbinit file to avoid having to enter it manually each time that GDB is used.

    Statically linked programs

    Even on recent Fedora releases (which all have glibc versions newer than 2.34), GDB will have trouble finding thread-local storage when the program is statically linked. For our example, this is accomplished by building the program as follows:

    gcc -Wall -g -static -o open-error ./open-error.c

    Then, when attempting to print out errno, a similar message is printed as shown earlier:

    (gdb) p errno
    Cannot find thread-local storage for process 72156, executable file /tmp/examp/open-error:
    Cannot find thread-local variables on this target

    As a workaround, the macro-define trick works for this case too:

    (gdb) macro define errno *(*(int *(*)(void)) __errno_location) ()
    (gdb) print errno
    $1 = 2

    Macro debuginfo in executable, but missing glibc debuginfo

    When the -g3 option is used with gcc or clang, the executable's debugging information will include information about preprocessor-defined macros. This can be demonstrated by building the example program as follows:

    gcc -Wall -g3 -o open-error ./open-error.c

    If we debug the program with GDB and run to a breakpoint placed on line 11, we will likely see the normal behavior in which errno can be printed as shown earlier. But, if glibc's debugging info is missing and debuginfod is disabled, we might see the following behavior instead:

    (gdb) p errno
    '__errno_location' has unknown return type; cast the call to its declared return type

    Should this happen, it may be useful to look at how errno is defined. This can be done using GDB's info macro command:

    (gdb) info macro errno
    Defined at /usr/include/errno.h:38
     included at /tmp/examp/./open-error.c:3
    #define errno (*__errno_location ())

    Here, errno is defined to be a call to __errno_location. The address obtained from that call is then dereferenced to provide the value of errno. But, due to missing glibc debugging information, GDB doesn't know the type of __errno_location.

    This problem may be fixed by either providing glibc debugging information as shown earlier (either by installing glibc-debuginfo package or by using debuginfod to load it on demand), or by using the macro-define trick which was also shown earlier.

     

    Info alert: Note

    If the executable's debugging information is examined (via readelf -w), it may be found that there should be sufficient type information provided for __errno_location in order to make a call to __errno_location without a cast. Unfortunately, GDB is ignoring this information. This is a bug in GDB.

    Core file debugging

    There may also be issues with printing errno when debugging a core file. To help illustrate these problems, I'll make a core file from within GDB using a binary compiled with -g3, which causes information about macros to be included in the executable's debugging information. Moreover, the machine in question does not have the glibc-debuginfo package installed, nor have I enabled debuginfod for this example.

    As before, not shown below, I'll start GDB, and then run to line 11. After that, shown below, I'll make a core file using GDB's gcore command:

    (gdb) gcore errno.core
    warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
    Saved corefile errno.core

    Then, starting GDB again, the core file can be debugged like this:

    $ gdb -q open-error errno.core
    Reading symbols from open-error...
    [New LWP 2564]
    
    This GDB supports auto-downloading debuginfo from the following URLs:
      <https://debuginfod.fedoraproject.org/>
    Enable debuginfod for this session? (y or [n]) n
    Debuginfod has been disabled.
    To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib64/libthread_db.so.1".
    Core was generated by `/tmp/examp/open-error'.
    Program terminated with signal SIGTRAP, Trace/breakpoint trap.
    #0  main (argc=1, argv=0x7fffffffdb88) at ./open-error.c:11
    11	  if (fd < 0)			/* Line 11: Set GDB breakpoint here.  */
    Missing debuginfo, try: dnf debuginfo-install glibc-2.39.9000-18.fc41.x86_64

    Now, let's try to print errno:

    (gdb) print errno
    You can't do that without a process to debug.

    GDB shows this message because it is actually trying to call __errno_location, but this cannot be done without a running process. You could try removing the definition of errno as shown below, but, as documented in the GDB manual, this won't work—GDB's macro undef command only works to remove definitions of macros defined from within GDB.

    (gdb) macro undef errno
    (gdb) info macro errno
    Defined at /usr/include/errno.h:38
     included at /tmp/examp/./open-error.c:3
    #define errno (*__errno_location ())

    What you can do, however, is to define errno as itself. Once that is done, print errno will work, though a cast might still be needed:

    (gdb) macro define errno errno
    (gdb) print errno
    'errno' has unknown type; cast it to its declared type
    (gdb) print (int) errno
    $1 = 2

    Core files with a statically linked executable

    A statically linked executable with debugging information containing macro names and their expansions may be created using this command:

    gcc -Wall -g3 -static -o open-error ./open-error.c

    If a core file is created as shown in the previous section, attempting to debug this core file might look like this:

    $ gdb -q -iex 'set debuginfod enabled off' open-error errno.core
    Reading symbols from open-error...
    [New LWP 2970]
    Core was generated by `/tmp/examp/open-error'.
    Program terminated with signal SIGTRAP, Trace/breakpoint trap.
    #0  main (argc=1, argv=0x7fffffffdb88) at ./open-error.c:11
    11      if (fd < 0)            /* Line 11: Set GDB breakpoint here.  */
    (gdb) p errno
    You can't do that without a process to debug.
    (gdb) macro define errno errno
    (gdb) p (int) errno
    Cannot find thread-local storage for LWP 2970, executable file /tmp/examp/open-error:
    Cannot find thread-local variables on this target

    When we had a running process, the trick that we used to get around this problem of not being able to find thread-local storage was to call __errno_location and then dereference the result. But that won't work here due to the fact that we're debugging a core file, not a running process. To the best of my knowledge, there is no way to print errno for this scenario.

    Summary

    This article discussed scenarios in which doing print errno when debugging with GDB doesn't work.

    GDB has a better chance of accessing errno when glibc debugging information is available to GDB. It can either be installed via a suitable command, such as sudo debuginfo-install glibc, or GDB can instead load it on demand using debuginfod. In order to do the latter, add the following line to your .gdbinit file:

    set debuginfod enabled on

    If GDB doesn't know the type of errno, it may be possible to print it by adding a cast, i.e. print (int) errno.

    When debugging a running process, but GDB can't find thread-local storage associated with errno, try defining errno as a macro within GDB, like this:

    macro define errno *(*(int *(*)(void)) __errno_location) ()

    When debugging a core file, it may be necessary to disable a macro defining errno like this:

    macro define errno errno

    Finally, there is at least one scenario in which printing errno is simply not possible.

    Related Posts

    • New C++ features in GCC 14

    • How the GNU C Library handles backward compatibility

    • Upgrading the GNU C Library within Red Hat Enterprise Linux

    • Debuginfo is not just for debugging programs

    • How lazy debuginfo loading improves GDB and Valgrind

    • Improvements to static analysis in the GCC 14 compiler

    Recent Posts

    • Protect data offloaded to GPU-accelerated environments with OpenShift sandboxed containers

    • Case study: Measuring energy efficiency on the x64 platform

    • How to prevent AI inference stack silent failures

    • Preventing GPU waste: A guide to JIT checkpointing with Kubeflow Trainer on OpenShift AI

    • How to manage TLS certificates used by OpenShift GitOps operator

    What’s up next?

    This cheat sheet covers the basics of installing .NET on Red Hat Enterprise Linux (RHEL), how to get a simple program running, and how to run a program in a Linux container.

    Get the cheat sheet
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.