dlsym(3)

πŸ’… dlsym(3) β€” bestie edition

okay so we're in section 3 of the manual pages (library functions, iykyk) and today's main character is dlsym, plus its lowkey famous cousin dlvsym. this is the whole "get me the address of a symbol inside a shared object" situation and honestly? iconic.

✨ NAME ✨

dlsym, dlvsym β€” obtain address of a symbol in a shared object or executable

πŸ“š LIBRARY πŸ“š

Dynamic linking library (libdl, -ldl)

like, that's the library you gotta link against, no cap.

πŸŽ€ SYNOPSIS πŸŽ€

#include <dlfcn.h>

void *dlsym(void *restrict handle, const char *restrict symbol);

#define _GNU_SOURCE
#include <dlfcn.h>

void *dlvsym(void *restrict handle, const char *restrict symbol,
             const char *restrict version);

note the second one needs _GNU_SOURCE defined first, she's fancy like that πŸ’…

πŸ¦‹ DESCRIPTION πŸ¦‹

so dlsym() takes a "handle" of a dynamically loaded shared object β€” the kind you get back from dlopen(3) β€” plus a null-terminated symbol name, and returns the address where that symbol got loaded into memory. that's it. that's the whole vibe. 🫢

if the symbol isn't found β€” not in the specified object, and not in any of the shared objects that dlopen(3) auto-loaded when that object was loaded β€” dlsym() returns NULL. and just so you know, the search it does is breadth first through the dependency tree of those shared objects. 🌈

⚠️ in unusual cases (peep the NOTES section, bestie) the value of the symbol could literally be NULL. so a NULL return from dlsym() does not automatically mean error. 😭

the correct way to tell an error apart from a symbol whose value is actually NULL: call dlerror(3) to clear any old error conditions, then call dlsym(), then call dlerror(3) again and save its return value into a variable, then check whether that saved value is not NULL. yes, it's three steps. yes, it's giving ritual. do it anyway. ✨

there are two special pseudo-handles you can pass in handle:

  • RTLD_DEFAULT β€” find the first occurrence of the symbol you want using the default shared object search order. the search includes global symbols in the executable and its dependencies, plus symbols in shared objects that were dynamically loaded with the RTLD_GLOBAL flag. πŸ’–
  • RTLD_NEXT β€” find the next occurrence of the symbol in the search order, after the current object. this is how you wrap a function that lives in another shared object, so that β€” for example β€” a function definition in a preloaded shared object (see LD_PRELOAD in ld.so(8)) can find and invoke the "real" function provided in another shared object. or, honestly, the "next" definition of the function in cases where there's multiple layers of preloading. layers, bestie. 🧚
🌸 the _GNU_SOURCE feature test macro must be defined in order to get the definitions of RTLD_DEFAULT and RTLD_NEXT out of <dlfcn.h>. no macro, no pseudo-handles, periodt.

and dlvsym() does the exact same thing as dlsym() but takes a version string as an additional argument. extra parameter, extra slay. πŸ‘‘

πŸ”₯ RETURN VALUE πŸ”₯

on success, these functions return the address associated with symbol. on failure, they return NULL, and the error can be diagnosed using dlerror(3). that's the deal. πŸ’€

πŸ‘‘ ATTRIBUTES πŸ‘‘

for an explanation of the terms used in this section, see attributes(7).

  • dlsym(), dlvsym() β€” Thread safety: MT-Safe

MT-Safe!! she's thread-safe, we love a stable queen. πŸ™ˆ

⭐ STANDARDS ⭐

  • dlsym() β€” POSIX.1-2008.
  • dlvsym() β€” GNU.

πŸ’œ HISTORY πŸ’œ

  • dlsym() β€” glibc 2.0. POSIX.1-2001.
  • dlvsym() β€” glibc 2.1.

🌸 NOTES 🌸

there are several scenarios when the address of a global symbol is NULL. like:

  • a symbol can be placed at zero address by the linker, via a linker script or with the --defsym command-line option.
  • undefined weak symbols also have NULL value.
  • finally, the symbol value may be the result of a GNU indirect function (IFUNC) resolver function that returns NULL as the resolved value. in this last case, dlsym() also returns NULL without error. 😌
⚠️ however, in the former two cases the behavior of the GNU dynamic linker is inconsistent: relocation processing succeeds and the symbol can be observed to have NULL value, but dlsym() fails and dlerror() indicates a lookup error. messy. inconsistent. not the vibe we deserve. 😭

πŸ“– History πŸ“–

the dlsym() function is part of the dlopen API, derived from SunOS. that system does not have dlvsym(). SunOS could never, fr fr. πŸ’…

πŸ“ EXAMPLES πŸ“

see dlopen(3). (go look, bestie, the examples live over there 🫢)

🌈 SEE ALSO 🌈

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


okay that's the whole page!! πŸŽ€ this was Linux man-pages 6.20, dated 2026-02-08, and the page itself is dlsym(3) β€” stay safe out there, clear your dlerror(3) before you check it, and remember: a NULL is not always a L. πŸ’–βœ¨

πŸ“¦ 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: GPL-2.0-or-later
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms