The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
dl.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 37717f9f82c82f4dbd6ede12de7e9f66a357e939 $
19 *
20 * @file src/lib/util/dl.c
21 * @brief Wrappers around dlopen to manage loading shared objects at runtime.
22 *
23 * @copyright 2016-2019 The FreeRADIUS server project
24 * @copyright 2016-2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 */
26RCSID("$Id: 37717f9f82c82f4dbd6ede12de7e9f66a357e939 $")
27
28#include <freeradius-devel/server/dl_module.h>
29#include <freeradius-devel/server/log.h>
30#include <freeradius-devel/util/atexit.h>
31#include <freeradius-devel/util/debug.h>
32
33#include <freeradius-devel/util/paths.h>
34#include <freeradius-devel/util/syserror.h>
35
36
37#ifdef HAVE_VALGRIND_H
38# include <valgrind.h>
39#else
40# define RUNNING_ON_VALGRIND 0
41#endif
42
43#ifndef RTLD_NOW
44# define RTLD_NOW (0)
45#endif
46#ifndef RTLD_LOCAL
47# define RTLD_LOCAL (0)
48#endif
49
50/** Symbol dependent initialisation callback
51 *
52 * Call this function when the dl is loaded for the first time.
53 */
56 fr_dlist_t entry; //!< Entry into the list of 'init' symbol callbacks.
57
58 unsigned int priority; //!< Call priority
59 char const *symbol; //!< to search for. May be NULL in which case func is always called.
60 dl_onload_t func; //!< to call when symbol is found in a dl's symbol table.
61 void *uctx; //!< User data to pass to func.
62};
63
64/** Symbol dependent free callback
65 *
66 * Call this function before the dl is unloaded.
67 */
70 fr_dlist_t entry; //!< Entry into the list of 'free' symbol callbacks.
71
72 unsigned int priority; //!< Call priority
73 char const *symbol; //!< to search for. May be NULL in which case func is always called.
74 dl_unload_t func; //!< to call when symbol is found in a dl's symbol table.
75 void *uctx; //!< User data to pass to func.
76};
77
78/** A dynamic loader
79 *
80 */
82 char *lib_dir; //!< Where the libraries live.
83
84 /** Linked list of symbol init callbacks
85 *
86 * @note Is linked list to retain insertion order. We don't expect huge numbers
87 * of callbacks so there shouldn't be efficiency issues.
88 */
90
91 /** Linked list of symbol free callbacks
92 *
93 * @note Is linked list to retain insertion order. We don't expect huge numbers
94 * of callbacks so there shouldn't be efficiency issues.
95 */
97
98 bool do_dlclose; //!< dlclose modules when we're done with them.
99
100 bool do_static; //!< Do all symbol resolution using the special
101 ///< RTLD_DEFAULT handle, instead of attempting
102 ///< to load modules using dlopen(). This is
103 ///< useful when FreeRADIUS has been built as
104 ///< a monolithic binary.
105
106 fr_rb_tree_t *tree; //!< Tree of shared objects loaded.
107
108 void *uctx; //!< dl private extension data.
109
110 bool uctx_free; //!< Free uctx when dl_loader_t is freed.
111
112 bool defer_symbol_init; //!< Do not call dl_symbol_init in dl_loader_init.
113};
114
115/** Global search path, prepended all dlopen paths
116 */
117static char *dl_global_libdir = NULL;
118
119static int8_t dl_symbol_init_cmp(void const *one, void const *two)
120{
121 dl_symbol_init_t const *a = one, *b = two;
122 int ret;
123
124 fr_assert(a && b);
125
126 ret = ((void *)a->func > (void *)b->func) - ((void *)a->func < (void *)b->func);
127 if (ret != 0) return ret;
128
129 ret = (a->symbol && !b->symbol) - (!a->symbol && b->symbol);
130 if (ret != 0) return ret;
131
132 if (!a->symbol && !b->symbol) return 0;
133
134#ifdef STATIC_ANALYZER
135 if (!fr_cond_assert(a->symbol && b->symbol)) return 0; /* Bug in clang scan ? */
136#endif
137
138 ret = strcmp(a->symbol, b->symbol);
139 return CMP(ret, 0);
140}
141
142static int8_t dl_symbol_free_cmp(void const *one, void const *two)
143{
144 dl_symbol_free_t const *a = one, *b = two;
145 int ret;
146
147 fr_assert(a && b);
148
149 ret = ((void *)a->func > (void *)b->func) - ((void *)a->func < (void *)b->func);
150 if (ret != 0) return ret;
151
152 ret = (a->symbol && !b->symbol) - (!a->symbol && b->symbol);
153 if (ret != 0) return ret;
154
155 if (!a->symbol && !b->symbol) return 0;
156
157#ifdef STATIC_ANALYZER
158 if (!fr_cond_assert(a->symbol && b->symbol)) return 0; /* Bug in clang scan ? */
159#endif
160
161 ret = strcmp(a->symbol, b->symbol);
162 return CMP(ret, 0);
163}
164
165/** Compare the name of two dl_t
166 *
167 */
168static int8_t dl_handle_cmp(void const *one, void const *two)
169{
170 int ret;
171
172 ret = strcmp(((dl_t const *)one)->name, ((dl_t const *)two)->name);
173 return CMP(ret, 0);
174}
175
176/** Utility function to dlopen the library containing a particular symbol
177 *
178 * @note Not really part of our 'dl' API, just a convenience function.
179 *
180 * @param[in] sym_name to resolve.
181 * @param[in] flags to pass to dlopen.
182 * @return
183 * - NULL on error.
184 * - A new handle on success.
185 */
186void *dl_open_by_sym(char const *sym_name, int flags)
187{
188 Dl_info info;
189 void *sym;
190 void *handle;
191
192 /*
193 * Resolve the test symbol in our own symbol space by
194 * iterating through all the libraries.
195 * This might be slow. Don't do this at runtime!
196 */
197 sym = dlsym(RTLD_DEFAULT, sym_name);
198 if (!sym) {
199 fr_strerror_printf("Can't resolve symbol %s", sym_name);
200 return NULL;
201 }
202
203 /*
204 * Lookup the library the symbol belongs to
205 */
206 if (dladdr(sym, &info) == 0) {
207 fr_strerror_printf("Failed retrieving info for \"%s\" (%p)", sym_name, sym);
208 return NULL;
209 }
210
211 handle = dlopen(info.dli_fname, flags);
212 if (!handle) {
213 fr_strerror_printf("Failed loading \"%s\": %s", info.dli_fname, dlerror());
214 return NULL;
215 }
216
217 return handle;
218}
219
220/** Walk over the registered init callbacks, searching for the symbols they depend on
221 *
222 * Allows code outside of the dl API to register initialisation functions that get
223 * executed depending on whether the dl exports a particular symbol.
224 *
225 * This cuts down the amount of boilerplate code in 'mod_load' functions.
226 *
227 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
228 * @param[in] dl to search for symbols in.
229 * @return
230 * - 0 continue walking.
231 * - -1 error.
232 */
234{
235 dl_symbol_init_t *init = NULL;
236 void *sym = NULL;
237 char buffer[256];
238
239 while ((init = fr_dlist_next(&dl_loader->sym_init, init))) {
240 if (init->symbol) {
241 char *p;
242
243 snprintf(buffer, sizeof(buffer), "%s_%s", dl->name, init->symbol);
244
245 /*
246 * '-' is not a valid symbol character in
247 * C. But "libfreeradius-radius" is a
248 * valid library name. So we hash things together.
249 */
250 for (p = buffer; *p != '\0'; p++) {
251 if (*p == '-') *p = '_';
252 }
253
254 sym = dlsym(dl->handle, buffer);
255 if (!sym) {
256 continue;
257 }
258 }
259
260 if (init->func(dl, sym, init->uctx) < 0) {
261 fr_strerror_printf("Initialiser \"%s\" failed", buffer);
262 return -1;
263 }
264 }
265
266 return 0;
267}
268
269/** Walk over the registered init callbacks, searching for the symbols they depend on
270 *
271 * Allows code outside of the dl API to register free functions that get
272 * executed depending on whether the dl exports a particular symbol.
273 *
274 * This cuts down the amount of boilerplate code in 'mod_unload' functions.
275 *
276 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
277 * @param[in] dl to search for symbols in.
278 */
280{
281 dl_symbol_free_t *free = NULL;
282 void *sym = NULL;
283
284 while ((free = fr_dlist_next(&dl_loader->sym_free, free))) {
285 if (free->symbol) {
286 char *sym_name = NULL;
287
288 sym_name = talloc_typed_asprintf(NULL, "%s_%s", dl->name, free->symbol);
289 if (!sym_name) return -1;
290
291 sym = dlsym(dl->handle, sym_name);
292 talloc_free(sym_name);
293
294 if (!sym) continue;
295 }
296
297 free->func(dl, sym, free->uctx);
298 }
299
300 return 0;
301}
302
303/** Register a callback to execute when a dl with a particular symbol is first loaded
304 *
305 * @note Will replace ctx data for callbacks with the same symbol/func.
306 *
307 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
308 * @param[in] priority Execution priority. Callbacks with a higher priority get
309 * called first.
310 * @param[in] symbol that determines whether func should be called. "<modname>_" is
311 * added as a prefix to the symbol. The prefix is added because
312 * some dls are loaded with RTLD_GLOBAL into the global symbol
313 * space, so the symbols they export must be unique.
314 * May be NULL to always call the function.
315 * @param[in] func to register. Called when dl is loaded.
316 * @param[in] uctx to pass to func.
317 * @return
318 * - 0 on success (or already registered).
319 * - -1 on failure.
320 */
322 char const *symbol, dl_onload_t func, void *uctx)
323{
325
327
328 n = talloc(dl_loader, dl_symbol_init_t);
329 if (unlikely(!n)) return -1;
330 *n = (dl_symbol_init_t){
331 .priority = priority,
332 .symbol = symbol,
333 .func = func,
334 .uctx = uctx
335 };
336
338 if (p->priority < priority) {
340 n = NULL;
341 break;
342 }
343 }
345
346 return 0;
347}
348
349/** Unregister an callback that was to be executed when a dl was first loaded
350 *
351 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
352 * @param[in] symbol the callback is attached to.
353 * @param[in] func the callback.
354 */
356{
357 dl_symbol_init_t *found = NULL, find = { .symbol = symbol, .func = func };
358
359 while ((found = fr_dlist_next(&dl_loader->sym_init, found)) && (dl_symbol_init_cmp(&find, found) != 0));
360 if (found) {
362 talloc_free(found);
363 }
364}
365
366/** Register a callback to execute when a dl with a particular symbol is unloaded
367 *
368 * @note Will replace ctx data for callbacks with the same symbol/func.
369 *
370 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
371 * @param[in] priority Execution priority. Callbacks with a higher priority get
372 * called first.
373 * @param[in] symbol that determines whether func should be called. "<modname>_" is
374 * added as a prefix to the symbol. The prefix is added because
375 * some dls are loaded with RTLD_GLOBAL into the global symbol
376 * space, so the symbols they export must be unique.
377 * May be NULL to always call the function.
378 * @param[in] func to register. Called then dl is unloaded.
379 * @param[in] uctx to pass to func.
380 * @return
381 * - 0 on success (or already registered).
382 * - -1 on failure.
383 */
385 char const *symbol, dl_unload_t func, void *uctx)
386{
388
390
391 n = talloc(dl_loader, dl_symbol_free_t);
392 if (!n) return -1;
393
394 *n = (dl_symbol_free_t){
395 .priority = priority,
396 .symbol = symbol,
397 .func = func,
398 .uctx = uctx
399 };
400
402 if (p->priority < priority) {
404 n = NULL;
405 break;
406 }
407 }
409
410 return 0;
411}
412
413/** Unregister an callback that was to be executed when a dl was unloaded
414 *
415 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
416 * @param[in] symbol the callback is attached to.
417 * @param[in] func the callback.
418 */
420{
421 dl_symbol_free_t *found = NULL, find = { .symbol = symbol, .func = func };
422
423 while ((found = fr_dlist_next(&dl_loader->sym_free, found)) && (dl_symbol_free_cmp(&find, found) != 0));
424 if (found) {
426 talloc_free(found);
427 }
428}
429
430/** Free a dl
431 *
432 * Close dl's dlhandle, unloading it.
433 *
434 * @param[in] dl to close.
435 * @return 0.
436 */
437static int _dl_free(dl_t *dl)
438{
439 dl = talloc_get_type_abort(dl, dl_t);
440
442
443 /*
444 * Only dlclose() handle if we're *NOT* running under valgrind
445 * as it unloads the symbols valgrind needs.
446 */
447 if (dl->loader->do_dlclose) dlclose(dl->handle); /* ignore any errors */
448
449 dl->handle = NULL;
450
452
453 return 0;
454}
455
456/** Search for a dl's shared object in various locations
457 *
458 * @note You must call dl_symbol_init when ready to call autoloader callbacks.
459 *
460 * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
461 * @param[in] name of library to load. May be a relative path.
462 * @param[in] uctx Data to store within the dl_t.
463 * @param[in] uctx_free talloc_free the passed in uctx data if this
464 * dl_t is freed.
465 * @return
466 * - A new dl_t on success, or a pointer to an existing
467 * one with the reference count increased.
468 * - NULL on error.
469 */
470dl_t *dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
471{
472 int flags = RTLD_NOW;
473 void *handle = NULL;
474 char const *search_path;
475 dl_t *dl;
476
477 /*
478 * There's already something in the tree,
479 * just return that instead.
480 */
481 dl = fr_rb_find(dl_loader->tree, &(dl_t){ .name = name });
482 if (dl) {
483 talloc_increase_ref_count(dl);
484 return dl;
485 }
486
487 /*
488 * Get a reference to the internal RTLD_HANDLE,
489 * which is used to search through all current loaded
490 * modules.
491 *
492 * This is useful for static builds which don't actually
493 * want to dlopen anything, and just want to look at the
494 * current global symbol table to load everything
495 * from there.
496 */
497 if (dl_loader->do_static) {
498 handle = dlopen(NULL, 0);
499 if (!handle) {
500 fr_strerror_printf("Failed opening RTLD_DEFAULT handle - %s", dlerror());
501 return NULL;
502 }
503 goto do_symbol_check;
504 }
505
506 flags |= RTLD_LOCAL;
507
508 /*
509 * Forces dlopened libraries to resolve symbols within
510 * their local symbol tables instead of the global symbol
511 * table.
512 *
513 * May help resolve issues with symbol conflicts.
514 */
515#if defined(RTLD_DEEPBIND) && !defined(__SANITIZE_ADDRESS__)
516 flags |= RTLD_DEEPBIND;
517#endif
518
519 fr_strerror_clear(); /* clear error buffer */
520
521 /*
522 * Bind all the symbols *NOW* so we don't hit errors later
523 */
524 flags |= RTLD_NOW;
525
526 search_path = dl_search_path(dl_loader);
527
528 /*
529 * Prefer loading our libraries by absolute path.
530 */
531 if (search_path) {
532 char *ctx, *paths, *path;
533 char *p;
534 char *dlerror_txt = NULL;
535
537
538 ctx = paths = talloc_typed_strdup(NULL, search_path);
539 while ((path = strsep(&paths, ":")) != NULL) {
540 /*
541 * Trim the trailing slash
542 */
543 p = strrchr(path, '/');
544 if (p && ((p[1] == '\0') || (p[1] == ':'))) *p = '\0';
545
546 path = talloc_typed_asprintf(ctx, "%s/%s%s", path, name, DL_EXTENSION);
547 handle = dlopen(path, flags);
548 talloc_free(path);
549 if (handle) break;
550
551 /*
552 * There's no dlopenat(), so one can't use it and later
553 * check acessatat() to avoid toctou.
554 *
555 * The only indication of why dlopen() failed is thus the
556 * contents of the string dlerror() returns, but all the
557 * man page says about that is that it's "human readable",
558 * NUL-terminated, and doesn't end with a newline.
559 *
560 * The following attempts to use the dlerror() output to
561 * determine whether dlopen() failed because path doesn't
562 * exist. This goes beyond what the API specifies (Hyrum's
563 * Law strikes again!), but the alternatives are
564 *
565 * 1. looking into the data structure dlerror() uses
566 * 2. let the toctou remain
567 *
568 * both of which seem worse.
569 */
570
571 /*
572 * If the file doesn't exist, continue with the next element
573 * of "path". Otherwise, stop looking for more libraries
574 * and instead complain about access permissions.
575 */
576 dlerror_txt = dlerror();
577
578 /*
579 * Yes, this really is the only way of getting the errno
580 * from the dlopen API.
581 */
582 if (strstr(dlerror_txt, fr_syserror_simple(ENOENT)) != NULL) {
583#ifndef __linux__
584 int access_mode = R_OK | X_OK;
585
586# ifdef AT_ACCESS
587 access_mode |= AT_ACCESS;
588# endif
589 if (access(path, access_mode) < 0 && errno == ENOENT) continue;
590#endif
591 fr_strerror_printf_push("Access check failed: %s", dlerror_txt);
592 break;
593 }
594
595 /*
596 * We're reliant on dlerror_txt from the loop,
597 * because once dlerror() is called the error
598 * is cleared.
599 *
600 * We need to push every error because we
601 * don't know which one would be useful
602 * in diagnosing the underlying cause of the
603 * load failure.
604 */
605 fr_strerror_printf_push("%s", dlerror_txt ? dlerror_txt : "unknown dlopen error");
606 }
607
608 /*
609 * No element of "path" had the library. Return
610 * the error from the last dlopen().
611 */
612 if (!handle) {
613 talloc_free(ctx);
614 return NULL;
615 }
616
617 fr_strerror_clear(); /* Don't leave spurious errors in the buffer */
618
619 talloc_free(ctx);
620 } else {
621 char buffer[2048];
622
623 strlcpy(buffer, name, sizeof(buffer));
624 /*
625 * FIXME: Make this configurable...
626 */
628
629 handle = dlopen(buffer, flags);
630 if (!handle) {
631 char *error = dlerror();
632
633 /*
634 * Append the error
635 */
636 fr_strerror_printf_push("%s", error);
637 return NULL;
638 }
639 }
640
641do_symbol_check:
642 dl = talloc(dl_loader, dl_t);
643 if (unlikely(!dl)) {
644 dlclose(handle);
645 return NULL;
646 }
647 *dl = (dl_t){
649 .handle = handle,
650 .loader = dl_loader,
651 .uctx = uctx,
652 .uctx_free = uctx_free
653 };
654 talloc_set_destructor(dl, _dl_free);
655
657 if (!dl->in_tree) {
659 return NULL;
660 }
661
663
664 return dl;
665}
666
667/** "free" a dl handle, possibly actually freeing it, and unloading the library
668 *
669 * This function should be used to explicitly free a dl.
670 *
671 * Because dls are reference counted, it may not actually free the memory
672 * or unload the library, but it will reduce the reference count.
673 *
674 * @return
675 * - 0 if the dl was actually freed.
676 * - >0 the number of remaining references.
677 */
678int dl_free(dl_t const *dl)
679{
680 if (!dl) return 0;
681
683}
684
686{
687 int ret = 0;
688
689 if (dl_loader->uctx_free) {
690 ret = talloc_free(dl_loader->uctx);
691 if (ret != 0) goto finish;
692 }
693
694 /*
695 * Prevent freeing if we still have dls loaded
696 * We do reference counting, we know exactly what
697 * should still be active.
698 */
700#ifndef NDEBUG
702 void *data;
703
704 /*
705 * Yes, this is the correct call order
706 */
708 data;
709 data = fr_rb_iter_next_inorder(&iter)) {
710 dl_t *dl = talloc_get_type_abort(data, dl_t);
711
712 fr_strerror_printf_push(" %s (%zu)", dl->name, talloc_reference_count(dl));
713 }
714
715 fr_strerror_printf_push("Refusing to cleanup dl loader, the following dynamically loaded "
716 "libraries are still in use:");
717#endif
718 ret = -1;
719 goto finish;
720 }
721
722finish:
723 return ret;
724}
725
726/** Return current library path
727 *
728 */
730{
731 char *env;
732 fr_sbuff_t *search_path = NULL;
733
734 /*
735 * The search path in this order [env:][global:]dl_loader->lib_dir
736 */
737
738 /*
739 * Create a thread-local extensible buffer to
740 * store library search_path data.
741 *
742 * This is created once per-thread (the first time
743 * this function is called), and freed when the
744 * thread exits.
745 */
746 FR_SBUFF_TALLOC_THREAD_LOCAL(&search_path, 16, PATH_MAX);
747
748 /*
749 * Apple removed support for DYLD_LIBRARY_PATH in rootless mode.
750 */
751 env = getenv("FR_LIBRARY_PATH");
752 if (env && fr_sbuff_in_sprintf(search_path, "%s:", env) < 0) return NULL;
753
754 if (dl_global_libdir && fr_sbuff_in_sprintf(search_path, "%s:", dl_global_libdir) < 0) return NULL;
755
756 if (fr_sbuff_in_strcpy(search_path, dl_loader->lib_dir) < 0) return NULL;
757
758 return fr_sbuff_start(search_path);
759}
760
761/** Set the global library path
762 *
763 * @param[in] lib_dir ":" separated list of paths to search for libraries in.
764 * @return
765 * - 0 on success.
766 * - -1 on failure.
767 */
768int dl_search_global_path_set(char const *lib_dir)
769{
770 if (dl_global_libdir) TALLOC_FREE(dl_global_libdir);
771
772 dl_global_libdir = talloc_typed_strdup(NULL, lib_dir);
773 if (!dl_global_libdir) {
774 fr_strerror_const("Failed allocating memory for global dl search path");
775 return -1;
776 }
777
778 fr_atexit_global_once(NULL, fr_atexit_talloc_free, dl_global_libdir);
779
780 return 0;
781}
782
783/** Set the current library path
784 *
785 * @param[in] dl_loader to add search path component for.
786 * @param[in] lib_dir A ":" separated list of paths to search for libraries in.
787 */
788int dl_search_path_set(dl_loader_t *dl_loader, char const *lib_dir)
789{
790 char const *old;
791
792 old = dl_loader->lib_dir;
793
794 dl_loader->lib_dir = talloc_strdup(dl_loader, lib_dir);
795 if (!dl_loader->lib_dir) {
796 fr_strerror_const("Failed allocating memory for dl search path");
797 return -1;
798 }
799
801
802 return 0;
803}
804
805/** Append a new search path component to the library search path
806 *
807 * @param[in] dl_loader to add search path component for.
808 * @param[in] lib_dir to add. Does not require a ":" prefix.
809 * @return
810 * - 0 on success.
811 * - -1 on failure.
812 */
814{
815 char *new;
816
817 if (!dl_loader->lib_dir) {
818 dl_loader->lib_dir = talloc_strdup(dl_loader->lib_dir, lib_dir);
819 if (!dl_loader->lib_dir) {
820 oom:
821 fr_strerror_const("Failed allocating memory for dl search path");
822 return -1;
823 }
824 return 0;
825 }
826
827 new = talloc_asprintf(dl_loader->lib_dir, "%s:%s", lib_dir, dl_loader->lib_dir);
828 if (!new) goto oom;
829
830 dl_loader->lib_dir = new;
831
832 return 0;
833}
834
835/** Append a new search path component to the library search path
836 *
837 * @param[in] dl_loader to add search path component for.
838 * @param[in] lib_dir to add. Does not require a ":" prefix.
839 * @return
840 * - 0 on success.
841 * - -1 on failure.
842 */
844{
845 char *new;
846
847 if (!dl_loader->lib_dir) {
848 dl_loader->lib_dir = talloc_strdup(dl_loader->lib_dir, lib_dir);
849 if (!dl_loader->lib_dir) {
850 oom:
851 fr_strerror_const("Failed allocating memory for dl search path");
852 return -1;
853 }
854 return 0;
855 }
856
857 new = talloc_asprintf_append_buffer(dl_loader->lib_dir, ":%s", lib_dir);
858 if (!new) goto oom;
859
860 dl_loader->lib_dir = new;
861
862 return 0;
863}
864
865/** Retrieve the uctx from a dl_loader
866 *
867 */
869{
870 return dl_loader->uctx;
871}
872
873/** Initialise structures needed by the dynamic linker
874 *
875 * @param[in] ctx To bind lifetime of dl_loader_t too.
876 * @param[in] uctx API client opaque data to store in dl_loader_t.
877 * @param[in] uctx_free Call talloc_free() on uctx when the dl_loader_t
878 * is freed.
879 * @param[in] defer_symbol_init If true, it is up to the caller to call
880 * #dl_symbol_init after calling #dl_by_name.
881 * This prevents any of the registered callbacks
882 * from executing until #dl_symbol_init is
883 * called explicitly.
884 */
885dl_loader_t *dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
886{
888
889 dl_loader = talloc_zero(NULL, dl_loader_t);
890 if (!dl_loader) {
891 fr_strerror_const("Failed allocating dl_loader");
892 return NULL;
893 }
894
896 if (!dl_loader->tree) {
897 fr_strerror_const("Failed initialising dl->tree");
898 error:
899 TALLOC_FREE(dl_loader);
900 return NULL;
901 }
902
904
906 if (!dl_loader->lib_dir) {
907 fr_strerror_const("Failed allocating memory for dl search path");
908 goto error;
909 }
910
911 talloc_set_destructor(dl_loader, _dl_loader_free);
912
913 /*
914 * Run this now to avoid bizarre issues
915 * with the talloc atexit handlers firing
916 * in the child, and that causing issues.
917 */
919 dl_loader->uctx = uctx;
920 dl_loader->uctx_free = uctx_free;
921 dl_loader->defer_symbol_init = defer_symbol_init;
922
923 /*
924 * It's not clear yet whether we still want
925 * some dynamic loading capability in
926 * emscripten, so keep this as a potentially
927 * runtime toggle for now.
928 */
929#ifdef __EMSCRIPTEN__
930 dl_loader->do_static = true;
931#else
932 dl_loader->do_static = false;
933#endif
936
937 return dl_loader;
938}
939
940/** Runtime override for doing static or dynamic module loading
941 *
942 * @param[in] dl_loader to configure.
943 * @param[in] do_static If true, all dlopen calls result in a
944 * reference to RTLD_DEFAULT being returned
945 * which allows all the dynamic loading
946 * infrastructure to worth correctly with
947 * a monolithic binary.
948 * @return The previous value of do_static
949 */
951{
952 bool old = dl_loader->do_static;
953
954 dl_loader->do_static = do_static;
955
956 return old;
957}
958
959/** Called from a debugger to print information about a dl_loader
960 *
961 */
963{
964 FR_FAULT_LOG("dl_loader %p", dl);
965 FR_FAULT_LOG("lib_dir : %s", dl->lib_dir);
966 FR_FAULT_LOG("do_dlclose : %s", dl->do_dlclose ? "yes" : "no");
967 FR_FAULT_LOG("uctx : %p", dl->uctx);
968 FR_FAULT_LOG("uctx_free : %s", dl->do_dlclose ? "yes" : "no");
969 FR_FAULT_LOG("defer_symbol_init : %s", dl->defer_symbol_init ? "yes" : "no");
970
971 fr_dlist_foreach(&dl->sym_init, dl_symbol_init_t, sym) {
972 FR_FAULT_LOG("symbol_init %s", sym->symbol ? sym->symbol : "<base>");
973 FR_FAULT_LOG("\tpriority : %u", sym->priority);
974 FR_FAULT_LOG("\tfunc : %p", sym->func);
975 FR_FAULT_LOG("\tuctx : %p", sym->uctx);
976 }
977
978 fr_dlist_foreach(&dl->sym_free, dl_symbol_free_t, sym) {
979 FR_FAULT_LOG("symbol_free %s", sym->symbol ? sym->symbol : "<base>");
980 FR_FAULT_LOG("\tpriority : %u", sym->priority);
981 FR_FAULT_LOG("\tfunc : %p", sym->func);
982 FR_FAULT_LOG("\tuctx : %p", sym->uctx);
983 }
984}
static int const char char buffer[256]
Definition acutest.h:576
int n
Definition acutest.h:577
#define fr_atexit_global_once(_init, _free, _uctx)
Definition atexit.h:211
static dl_loader_t * dl_loader
Definition fuzzer.c:43
static dl_t * dl
Definition fuzzer.c:42
static bool init
Definition fuzzer.c:41
#define RCSID(id)
Definition build.h:483
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define unlikely(_x)
Definition build.h:381
int fr_get_lsan_state(void)
Definition debug.c:261
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:49
struct dl_symbol_free_s dl_symbol_free_t
Symbol dependent free callback.
Definition dl.c:68
struct dl_symbol_init_s dl_symbol_init_t
Symbol dependent initialisation callback.
Definition dl.c:54
bool do_dlclose
dlclose modules when we're done with them.
Definition dl.c:98
dl_loader_t * dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
Initialise structures needed by the dynamic linker.
Definition dl.c:885
#define RTLD_LOCAL
Definition dl.c:47
char const * symbol
to search for. May be NULL in which case func is always called.
Definition dl.c:59
void * uctx
dl private extension data.
Definition dl.c:108
dl_onload_t func
to call when symbol is found in a dl's symbol table.
Definition dl.c:60
bool uctx_free
Free uctx when dl_loader_t is freed.
Definition dl.c:110
int dl_search_path_append(dl_loader_t *dl_loader, char const *lib_dir)
Append a new search path component to the library search path.
Definition dl.c:843
static char * dl_global_libdir
Global search path, prepended all dlopen paths.
Definition dl.c:117
int dl_search_global_path_set(char const *lib_dir)
Set the global library path.
Definition dl.c:768
int dl_symbol_free_cb_register(dl_loader_t *dl_loader, unsigned int priority, char const *symbol, dl_unload_t func, void *uctx)
Register a callback to execute when a dl with a particular symbol is unloaded.
Definition dl.c:384
static int _dl_free(dl_t *dl)
Free a dl.
Definition dl.c:437
bool dl_loader_set_static(dl_loader_t *dl_loader, bool do_static)
Runtime override for doing static or dynamic module loading.
Definition dl.c:950
static int8_t dl_symbol_init_cmp(void const *one, void const *two)
Definition dl.c:119
int dl_search_path_prepend(dl_loader_t *dl_loader, char const *lib_dir)
Append a new search path component to the library search path.
Definition dl.c:813
void * dl_open_by_sym(char const *sym_name, int flags)
Utility function to dlopen the library containing a particular symbol.
Definition dl.c:186
static int8_t dl_symbol_free_cmp(void const *one, void const *two)
Definition dl.c:142
fr_rb_tree_t * tree
Tree of shared objects loaded.
Definition dl.c:106
void * uctx
User data to pass to func.
Definition dl.c:75
static int _dl_loader_free(dl_loader_t *dl_loader)
Definition dl.c:685
void * uctx
User data to pass to func.
Definition dl.c:61
char const * dl_search_path(dl_loader_t *dl_loader)
Return current library path.
Definition dl.c:729
int dl_symbol_init_cb_register(dl_loader_t *dl_loader, unsigned int priority, char const *symbol, dl_onload_t func, void *uctx)
Register a callback to execute when a dl with a particular symbol is first loaded.
Definition dl.c:321
static int8_t dl_handle_cmp(void const *one, void const *two)
Compare the name of two dl_t.
Definition dl.c:168
int dl_search_path_set(dl_loader_t *dl_loader, char const *lib_dir)
Set the current library path.
Definition dl.c:788
#define RTLD_NOW
Definition dl.c:44
#define RUNNING_ON_VALGRIND
Definition dl.c:40
fr_dlist_t entry
Entry into the list of 'free' symbol callbacks.
Definition dl.c:70
bool do_static
Do all symbol resolution using the special RTLD_DEFAULT handle, instead of attempting to load modules...
Definition dl.c:100
void * dl_loader_uctx(dl_loader_t *dl_loader)
Retrieve the uctx from a dl_loader.
Definition dl.c:868
int dl_free(dl_t const *dl)
"free" a dl handle, possibly actually freeing it, and unloading the library
Definition dl.c:678
int dl_symbol_init(dl_loader_t *dl_loader, dl_t const *dl)
Walk over the registered init callbacks, searching for the symbols they depend on.
Definition dl.c:233
char * lib_dir
Where the libraries live.
Definition dl.c:82
bool defer_symbol_init
Do not call dl_symbol_init in dl_loader_init.
Definition dl.c:112
unsigned int priority
Call priority.
Definition dl.c:72
fr_dlist_t entry
Entry into the list of 'init' symbol callbacks.
Definition dl.c:56
void dl_symbol_free_cb_unregister(dl_loader_t *dl_loader, char const *symbol, dl_unload_t func)
Unregister an callback that was to be executed when a dl was unloaded.
Definition dl.c:419
void dl_symbol_init_cb_unregister(dl_loader_t *dl_loader, char const *symbol, dl_onload_t func)
Unregister an callback that was to be executed when a dl was first loaded.
Definition dl.c:355
dl_t * dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
Search for a dl's shared object in various locations.
Definition dl.c:470
void dl_loader_debug(dl_loader_t *dl)
Called from a debugger to print information about a dl_loader.
Definition dl.c:962
fr_dlist_head_t sym_init
Linked list of symbol init callbacks.
Definition dl.c:89
dl_unload_t func
to call when symbol is found in a dl's symbol table.
Definition dl.c:74
fr_dlist_head_t sym_free
Linked list of symbol free callbacks.
Definition dl.c:96
char const * symbol
to search for. May be NULL in which case func is always called.
Definition dl.c:73
static int dl_symbol_free(dl_loader_t *dl_loader, dl_t const *dl)
Walk over the registered init callbacks, searching for the symbols they depend on.
Definition dl.c:279
unsigned int priority
Call priority.
Definition dl.c:58
A dynamic loader.
Definition dl.c:81
bool in_tree
Whether this dl is registered in the dl_tree.
Definition dl.h:67
dl_loader_t * loader
Loader that owns this dl.
Definition dl.h:63
struct dl_s dl_t
Module handle.
int(* dl_onload_t)(dl_t const *module, void *symbol, void *user_ctx)
Callback to call when a module is first loaded.
Definition dl.h:79
void * handle
Handle returned by dlopen.
Definition dl.h:62
void * uctx
API client's opaque data.
Definition dl.h:65
char const * name
Name of the module e.g. sql.
Definition dl.h:61
void(* dl_unload_t)(dl_t const *module, void *symbol, void *user_ctx)
Callback when a module is destroyed.
Definition dl.h:88
Module handle.
Definition dl.h:58
#define DL_EXTENSION
Definition dl_module.h:57
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:260
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:94
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
static int fr_dlist_insert_before(fr_dlist_head_t *list_head, void *pos, void *ptr)
Insert an item before an item already in the list.
Definition dlist.h:450
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
free(array)
talloc_free(reap)
char * strsep(char **stringp, char const *delim)
Definition missing.c:123
char const * fr_path_default_lib_dir(void)
Return the default lib dir.
Definition paths.c:43
#define fr_assert(_expr)
Definition rad_assert.h:38
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:781
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition rb.c:824
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:741
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:246
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:321
The main red black tree structure.
Definition rb.h:73
static char const * name
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1454
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1595
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
size_t strlcat(char *dst, char const *src, size_t siz)
Definition strlcat.c:35
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
char const * fr_syserror_simple(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:291
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:492
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition talloc.c:171
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
int talloc_decrease_ref_count(void const *ptr)
Decrease the reference count on a ptr.
Definition talloc.c:820
#define talloc_get_type_abort_const
Definition talloc.h:282
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:224
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1265