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) thathandleis loaded in.RTLD_DI_LINKMAP(struct link_map **) β get a pointer to thelink_mapstructure that corresponds tohandle.infohere points to a pointer to alink_mapstructure, 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 tohandleinto whereverinfopoints π.RTLD_DI_SERINFO(Dl_serinfo *) β get the library search paths for the shared object thathandlerefers to.infois a pointer to aDl_serinfoholding those search paths. But here's the thing: the number of search paths can change, so the size of the structure thatinfopoints to can change too π. That's literally why theRTLD_DI_SERINFOSIZErequest exists β it lets apps size the buffer correctly. The caller has to do these steps:- Use a
RTLD_DI_SERINFOSIZErequest to populate aDl_serinfostructure with the size (dls_size) of the structure needed for the laterRTLD_DI_SERINFOrequest. - Allocate a
Dl_serinfobuffer of the correct size (dls_size). - Use a further
RTLD_DI_SERINFOSIZErequest to populate thedls_sizeanddls_cntfields of the buffer you allocated in the previous step. - Use a
RTLD_DI_SERINFOto get the library search paths.
- Use a
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 thedls_sizeanddls_cntfields of theDl_serinfostructure thatinfopoints to with values suitable for allocating a buffer for a laterRTLD_DI_SERINFOrequest β¨.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 aPT_TLSsegment, or if the calling thread hasn't allocated a block for it,NULLis 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. Thisdlinfocall 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
- Copyright 2015, Michael Kerrisk <mtk.manpages@gmail.com>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms