dlinfo(3)

✨ NAME ✨

okay bestie, say hi to dlinfo β€” it's the girl who pulls up and tells you literally everything about a dynamically loaded object πŸ‘‘ (and this page lives in section 3 of the Linux manual, i.e. dlinfo(3) πŸ’…)

πŸ“š LIBRARY πŸ“š

the Dynamic linking library, aka libdl, aka compile with -ldl 🫢

πŸŽ€ SYNOPSIS πŸŽ€

#define _GNU_SOURCE
#include <link.h>
#include <dlfcn.h>

int dlinfo(void *restrict handle, int request, void *restrict info);

🌸 DESCRIPTION 🌸

so dlinfo() gets you the tea about the dynamically loaded object that handle is pointing at β€” and that handle is usually something you snagged earlier from dlopen(3) or dlmopen(3) 🧚. The request argument is you deciding which piece of info you want. The info argument is a pointer to a buffer where the answer gets dropped; the type of that buffer depends on request, so pay attention, bestie.

Here's every request value that's supported, with the matching info type chillin' in parentheses:

  • RTLD_DI_LMID (Lmid_t *) β€” grab the ID of the link-map list (the namespace, iykyk) that handle is loaded in.
  • RTLD_DI_LINKMAP (struct link_map **) β€” get a pointer to the link_map structure that corresponds to handle. info here points to a pointer to a link_map structure, and <link.h> defines it like this:
struct link_map {
    ElfW(Addr) l_addr;  /* Difference between the
                           address in the ELF file and
                           the address in memory */
    char      *l_name;  /* Absolute pathname where
                           object was found */
    ElfW(Dyn) *l_ld;    /* Dynamic section of the
                           shared object */
    struct link_map *l_next, *l_prev;
                        /* Chain of loaded objects */

    /* Plus additional fields private to the
       implementation */
};
  • RTLD_DI_ORIGIN (char *) β€” copy the pathname of the origin of the shared object corresponding to handle into wherever info points πŸ’–.
  • RTLD_DI_SERINFO (Dl_serinfo *) β€” get the library search paths for the shared object that handle refers to. info is a pointer to a Dl_serinfo holding those search paths. But here's the thing: the number of search paths can change, so the size of the structure that info points to can change too 😭. That's literally why the RTLD_DI_SERINFOSIZE request exists β€” it lets apps size the buffer correctly. The caller has to do these steps:
    1. Use a RTLD_DI_SERINFOSIZE request to populate a Dl_serinfo structure with the size (dls_size) of the structure needed for the later RTLD_DI_SERINFO request.
    2. Allocate a Dl_serinfo buffer of the correct size (dls_size).
    3. Use a further RTLD_DI_SERINFOSIZE request to populate the dls_size and dls_cnt fields of the buffer you allocated in the previous step.
    4. Use a RTLD_DI_SERINFO to get the library search paths.

And the Dl_serinfo structure is defined like this:

typedef struct {
    size_t dls_size;           /* Size in bytes of
                                  the whole buffer */
    unsigned int dls_cnt;      /* Number of elements
                                  in 'dls_serpath' */
    Dl_serpath dls_serpath[1]; /* Actually longer,
                                  'dls_cnt' elements */
} Dl_serinfo;

Every one of those dls_serpath elements in the structure above is itself a structure shaped like this:

typedef struct {
    char *dls_name;            /* Name of library search
                                  path directory */
    unsigned int dls_flags;    /* Indicates where this
                                  directory came from */
} Dl_serpath;

The dls_flags field is currently unused and always contains zero. not me checking a field that never does anything πŸ™ˆ

  • RTLD_DI_SERINFOSIZE (Dl_serinfo *) β€” fill in the dls_size and dls_cnt fields of the Dl_serinfo structure that info points to with values suitable for allocating a buffer for a later RTLD_DI_SERINFO request ✨.
  • RTLD_DI_TLS_MODID (size_t *, since glibc 2.4) β€” get the module ID of this shared object's TLS (thread-local storage) segment, as used in TLS relocations. If this object doesn't define a TLS segment, zero gets placed in *info.
  • RTLD_DI_TLS_DATA (void **, since glibc 2.4) β€” get a pointer to the calling thread's TLS block corresponding to this shared object's TLS segment. If this object doesn't define a PT_TLS segment, or if the calling thread hasn't allocated a block for it, NULL is placed in *info.
  • RTLD_DI_PHDR (const ElfW(Phdr *), since glibc 2.34.1) β€” get the address of this shared object's program header and drop it in *info. This dlinfo call returns the number of program headers in the shared object πŸ‘‘

πŸ’« RETURN VALUE πŸ’«

when it slays: dlinfo() returns 0 (unless it's specified explicitly otherwise), or a positive value corresponding to the request. When it does NOT slay: it returns -1, and you can diagnose that error with dlerror(3) 😭

🌈 ATTRIBUTES 🌈

for the full explanation of the terms used in this section, go see attributes(7) πŸ’œ

  • Interface: dlinfo()
  • Attribute: Thread safety
  • Value: MT-Safe

(translation: she's MT-Safe, so she plays nice with threads, periodt πŸ’…)

πŸ¦‹ VERSIONS πŸ¦‹

the sets of requests supported by the various implementations overlaps only partially. so like, not every implementation supports the same list, bestie, mind the overlap πŸ“

⭐ STANDARDS ⭐

GNU. that's it, that's the standard πŸŽ€

πŸ•°οΈ HISTORY πŸ•°οΈ

glibc 2.3.3. Solaris. she's been iconic for a minute πŸ’–

🌟 EXAMPLES 🌟

the program below opens a shared objects using dlopen(3) and then uses the RTLD_DI_SERINFOSIZE and RTLD_DI_SERINFO requests to obtain the library search path list for the library. Here's an example of what we might see when we run the program:

$ ./a.out /lib64/libm.so.6;
dls_serpath[0].dls_name = /lib64
dls_serpath[1].dls_name = /usr/lib64

🧚 Program source 🧚

#define _GNU_SOURCE
#include <dlfcn.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
    void *handle;
    Dl_serinfo serinfo;
    Dl_serinfo *sip;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <libpath>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Obtain a handle for shared object specified on command line.  */

    handle = dlopen(argv[1], RTLD_NOW);
    if (handle == NULL) {
        fprintf(stderr, "dlopen() failed: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    /* Discover the size of the buffer that we must pass to
       RTLD_DI_SERINFO.  */

    if (dlinfo(handle, RTLD_DI_SERINFOSIZE, &serinfo) == -1) {
        fprintf(stderr, "RTLD_DI_SERINFOSIZE failed: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    /* Allocate the buffer for use with RTLD_DI_SERINFO.  */

    sip = malloc(serinfo.dls_size);
    if (sip == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    /* Initialize the 'dls_size' and 'dls_cnt' fields in the newly
       allocated buffer.  */

    if (dlinfo(handle, RTLD_DI_SERINFOSIZE, sip) == -1) {
        fprintf(stderr, "RTLD_DI_SERINFOSIZE failed: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    /* Fetch and print library search list.  */

    if (dlinfo(handle, RTLD_DI_SERINFO, sip) == -1) {
        fprintf(stderr, "RTLD_DI_SERINFO failed: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    for (size_t j = 0; j < serinfo.dls_cnt; j++)
        printf("dls_serpath[%zu].dls_name = %s\n",
               j, sip->dls_serpath[j].dls_name);

    exit(EXIT_SUCCESS);
}

πŸ‘‘ SEE ALSO πŸ‘‘

dl_iterate_phdr(3), dladdr(3), dlerror(3), dlopen(3), dlsym(3), ld.so(8)


and that's the whole page, bestie, no crumbs left 🌸 this cutie came to you straight from Linux man-pages 6.20, dated 2026-02-08. go forth and dlinfo() responsibly πŸ’–βœ¨πŸ«Ά

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ“œ the page's license: Linux-man-pages-copyleft
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms