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: ead4112450980caff41db4b5519112b5b750fc87 $
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: ead4112450980caff41db4b5519112b5b750fc87 $")
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 char *fullpath;
541
542 /*
543 * Trim the trailing slash
544 */
545 p = strrchr(path, '/');
546 if (p && ((p[1] == '\0') || (p[1] == ':'))) *p = '\0';
547
548 fullpath = talloc_typed_asprintf(ctx, "%s/%s%s", path, name, DL_EXTENSION);
549 handle = dlopen(fullpath, flags);
550 talloc_free(fullpath);
551 if (handle) break;
552
553 /*
554 * There's no dlopenat(), so one can't use it and later
555 * check acessatat() to avoid toctou.
556 *
557 * The only indication of why dlopen() failed is thus the
558 * contents of the string dlerror() returns, but all the
559 * man page says about that is that it's "human readable",
560 * NUL-terminated, and doesn't end with a newline.
561 *
562 * The following attempts to use the dlerror() output to
563 * determine whether dlopen() failed because path doesn't
564 * exist. This goes beyond what the API specifies (Hyrum's
565 * Law strikes again!), but the alternatives are
566 *
567 * 1. looking into the data structure dlerror() uses
568 * 2. let the toctou remain
569 *
570 * both of which seem worse.
571 */
572
573 /*
574 * If the file doesn't exist, continue with the next element
575 * of "path". Otherwise, stop looking for more libraries
576 * and instead complain about access permissions.
577 */
578 dlerror_txt = dlerror();
579 if (!dlerror_txt) {
580 fr_strerror_printf_push("Unknown error when trying directory %s", path);
581 continue;
582 }
583
584 /*
585 * Yes, this really is the only way of getting the errno
586 * from the dlopen API.
587 */
588 if (strstr(dlerror_txt, fr_syserror_simple(ENOENT)) != NULL) {
589#ifndef __linux__
590 int access_mode = R_OK | X_OK;
591
592# ifdef AT_ACCESS
593 access_mode |= AT_ACCESS;
594# endif
595 if ((access(path, access_mode) < 0) && (errno == ENOENT)) continue;
596#endif
597 fr_strerror_printf_push("Access check failed: %s", dlerror_txt);
598 break;
599 }
600
601 /*
602 * We're reliant on dlerror_txt from the loop,
603 * because once dlerror() is called the error
604 * is cleared.
605 *
606 * We need to push every error because we
607 * don't know which one would be useful
608 * in diagnosing the underlying cause of the
609 * load failure.
610 *
611 * However, OSX doesn't clear the error,
612 * it simply appends to it. We don't
613 * want endless amounts of duplication,
614 * so we tidy it up here.
615 */
616#ifndef __APPLE__
617 fr_strerror_const_push(dlerror_txt);
618#else
619 fr_strerror_const(dlerror_txt);
620#endif
621 }
622
623 /*
624 * No element of "path" had the library. Return
625 * the error from the last dlopen().
626 */
627 if (!handle) {
628 talloc_free(ctx);
629 return NULL;
630 }
631
632 fr_strerror_clear(); /* Don't leave spurious errors in the buffer */
633
634 talloc_free(ctx);
635 } else {
636 char buffer[2048];
637
638 strlcpy(buffer, name, sizeof(buffer));
639 /*
640 * FIXME: Make this configurable...
641 */
643
644 handle = dlopen(buffer, flags);
645 if (!handle) {
646 char *error = dlerror();
647
648 /*
649 * Append the error
650 */
651 fr_strerror_printf_push("%s", error);
652 return NULL;
653 }
654 }
655
656do_symbol_check:
657 dl = talloc(dl_loader, dl_t);
658 if (unlikely(!dl)) {
659 dlclose(handle);
660 return NULL;
661 }
662 *dl = (dl_t){
664 .handle = handle,
665 .loader = dl_loader,
666 .uctx = uctx,
667 .uctx_free = uctx_free
668 };
669 talloc_set_destructor(dl, _dl_free);
670
672 if (!dl->in_tree) {
674 return NULL;
675 }
676
678
679 return dl;
680}
681
682/** "free" a dl handle, possibly actually freeing it, and unloading the library
683 *
684 * This function should be used to explicitly free a dl.
685 *
686 * Because dls are reference counted, it may not actually free the memory
687 * or unload the library, but it will reduce the reference count.
688 *
689 * @return
690 * - 0 if the dl was actually freed.
691 * - >0 the number of remaining references.
692 */
693int dl_free(dl_t const *dl)
694{
695 if (!dl) return 0;
696
698}
699
701{
702 int ret = 0;
703
704 if (dl_loader->uctx_free) {
705 ret = talloc_free(dl_loader->uctx);
706 if (ret != 0) goto finish;
707 }
708
709 /*
710 * Prevent freeing if we still have dls loaded
711 * We do reference counting, we know exactly what
712 * should still be active.
713 */
715#ifndef NDEBUG
717 void *data;
718
719 /*
720 * Yes, this is the correct call order
721 */
723 data;
725 dl_t *dl = talloc_get_type_abort(data, dl_t);
726
727 fr_strerror_printf_push(" %s (%zu)", dl->name, talloc_reference_count(dl));
728 }
729
730 fr_strerror_printf_push("Refusing to cleanup dl loader, the following dynamically loaded "
731 "libraries are still in use:");
732#endif
733 ret = -1;
734 goto finish;
735 }
736
737finish:
738 return ret;
739}
740
741/** Return current library path
742 *
743 */
745{
746 char *env;
747 fr_sbuff_t *search_path = NULL;
748
749 /*
750 * The search path in this order [env:][global:]dl_loader->lib_dir
751 */
752
753 /*
754 * Create a thread-local extensible buffer to
755 * store library search_path data.
756 *
757 * This is created once per-thread (the first time
758 * this function is called), and freed when the
759 * thread exits.
760 */
761 FR_SBUFF_TALLOC_THREAD_LOCAL(&search_path, 16, PATH_MAX);
762
763 /*
764 * Apple removed support for DYLD_LIBRARY_PATH in rootless mode.
765 */
766 env = getenv("FR_LIBRARY_PATH");
767 if (env && fr_sbuff_in_sprintf(search_path, "%s:", env) < 0) return NULL;
768
769 if (dl_global_libdir && fr_sbuff_in_sprintf(search_path, "%s:", dl_global_libdir) < 0) return NULL;
770
771 if (fr_sbuff_in_strcpy(search_path, dl_loader->lib_dir) < 0) return NULL;
772
773 return fr_sbuff_start(search_path);
774}
775
776/** Set the global library path
777 *
778 * @param[in] lib_dir ":" separated list of paths to search for libraries in.
779 * @return
780 * - 0 on success.
781 * - -1 on failure.
782 */
783int dl_search_global_path_set(char const *lib_dir)
784{
785 if (dl_global_libdir) TALLOC_FREE(dl_global_libdir);
786
787 dl_global_libdir = talloc_typed_strdup(NULL, lib_dir);
788 if (!dl_global_libdir) {
789 fr_strerror_const("Failed allocating memory for global dl search path");
790 return -1;
791 }
792
793 fr_atexit_global_once(NULL, fr_atexit_talloc_free, dl_global_libdir);
794
795 return 0;
796}
797
798/** Set the current library path
799 *
800 * @param[in] dl_loader to add search path component for.
801 * @param[in] lib_dir A ":" separated list of paths to search for libraries in.
802 */
803int dl_search_path_set(dl_loader_t *dl_loader, char const *lib_dir)
804{
805 char const *old;
806
807 old = dl_loader->lib_dir;
808
809 dl_loader->lib_dir = talloc_strdup(dl_loader, lib_dir);
810 if (!dl_loader->lib_dir) {
811 fr_strerror_const("Failed allocating memory for dl search path");
812 return -1;
813 }
814
816
817 return 0;
818}
819
820/** Append a new search path component to the library search path
821 *
822 * @param[in] dl_loader to add search path component for.
823 * @param[in] lib_dir to add. Does not require a ":" prefix.
824 * @return
825 * - 0 on success.
826 * - -1 on failure.
827 */
829{
830 char *new;
831
832 if (!dl_loader->lib_dir) {
833 dl_loader->lib_dir = talloc_strdup(dl_loader->lib_dir, lib_dir);
834 if (!dl_loader->lib_dir) {
835 oom:
836 fr_strerror_const("Failed allocating memory for dl search path");
837 return -1;
838 }
839 return 0;
840 }
841
842 new = talloc_asprintf(dl_loader->lib_dir, "%s:%s", lib_dir, dl_loader->lib_dir);
843 if (!new) goto oom;
844
845 dl_loader->lib_dir = new;
846
847 return 0;
848}
849
850/** Append a new search path component to the library search path
851 *
852 * @param[in] dl_loader to add search path component for.
853 * @param[in] lib_dir to add. Does not require a ":" prefix.
854 * @return
855 * - 0 on success.
856 * - -1 on failure.
857 */
859{
860 char *new;
861
862 if (!dl_loader->lib_dir) {
863 dl_loader->lib_dir = talloc_strdup(dl_loader->lib_dir, lib_dir);
864 if (!dl_loader->lib_dir) {
865 oom:
866 fr_strerror_const("Failed allocating memory for dl search path");
867 return -1;
868 }
869 return 0;
870 }
871
872 new = talloc_asprintf_append_buffer(dl_loader->lib_dir, ":%s", lib_dir);
873 if (!new) goto oom;
874
875 dl_loader->lib_dir = new;
876
877 return 0;
878}
879
880/** Retrieve the uctx from a dl_loader
881 *
882 */
884{
885 return dl_loader->uctx;
886}
887
888/** Initialise structures needed by the dynamic linker
889 *
890 * @param[in] ctx To bind lifetime of dl_loader_t too.
891 * @param[in] uctx API client opaque data to store in dl_loader_t.
892 * @param[in] uctx_free Call talloc_free() on uctx when the dl_loader_t
893 * is freed.
894 * @param[in] defer_symbol_init If true, it is up to the caller to call
895 * #dl_symbol_init after calling #dl_by_name.
896 * This prevents any of the registered callbacks
897 * from executing until #dl_symbol_init is
898 * called explicitly.
899 */
900dl_loader_t *dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
901{
903
904 dl_loader = talloc_zero(NULL, dl_loader_t);
905 if (!dl_loader) {
906 fr_strerror_const("Failed allocating dl_loader");
907 return NULL;
908 }
909
911 if (!dl_loader->tree) {
912 fr_strerror_const("Failed initialising dl->tree");
913 error:
914 TALLOC_FREE(dl_loader);
915 return NULL;
916 }
917
919
921 if (!dl_loader->lib_dir) {
922 fr_strerror_const("Failed allocating memory for dl search path");
923 goto error;
924 }
925
926 talloc_set_destructor(dl_loader, _dl_loader_free);
927
928 /*
929 * Run this now to avoid bizarre issues
930 * with the talloc atexit handlers firing
931 * in the child, and that causing issues.
932 */
934 dl_loader->uctx = uctx;
935 dl_loader->uctx_free = uctx_free;
936 dl_loader->defer_symbol_init = defer_symbol_init;
937
938 /*
939 * It's not clear yet whether we still want
940 * some dynamic loading capability in
941 * emscripten, so keep this as a potentially
942 * runtime toggle for now.
943 */
944#ifdef __EMSCRIPTEN__
945 dl_loader->do_static = true;
946#else
947 dl_loader->do_static = false;
948#endif
951
952 return dl_loader;
953}
954
955/** Runtime override for doing static or dynamic module loading
956 *
957 * @param[in] dl_loader to configure.
958 * @param[in] do_static If true, all dlopen calls result in a
959 * reference to RTLD_DEFAULT being returned
960 * which allows all the dynamic loading
961 * infrastructure to worth correctly with
962 * a monolithic binary.
963 * @return The previous value of do_static
964 */
966{
967 bool old = dl_loader->do_static;
968
969 dl_loader->do_static = do_static;
970
971 return old;
972}
973
974/** Called from a debugger to print information about a dl_loader
975 *
976 */
978{
979 fprintf(fp, "dl_loader %p", dl);
980 fprintf(fp, "lib_dir : %s\n", dl->lib_dir);
981 fprintf(fp, "do_dlclose : %s\n", dl->do_dlclose ? "yes" : "no");
982 fprintf(fp, "uctx : %p\n", dl->uctx);
983 fprintf(fp, "uctx_free : %s\n", dl->do_dlclose ? "yes" : "no");
984 fprintf(fp, "defer_symbol_init : %s\n", dl->defer_symbol_init ? "yes" : "no");
985
986 fr_dlist_foreach(&dl->sym_init, dl_symbol_init_t, sym) {
987 fprintf(fp, "symbol_init %s\n", sym->symbol ? sym->symbol : "<base>");
988 fprintf(fp, "\tpriority : %u\n", sym->priority);
989 fprintf(fp, "\tfunc : %p\n", sym->func);
990 fprintf(fp, "\tuctx : %p\n", sym->uctx);
991 }
992
993 fr_dlist_foreach(&dl->sym_free, dl_symbol_free_t, sym) {
994 fprintf(fp, "symbol_free %s\n", sym->symbol ? sym->symbol : "<base>");
995 fprintf(fp, "\tpriority : %u\n", sym->priority);
996 fprintf(fp, "\tfunc : %p\n", sym->func);
997 fprintf(fp, "\tuctx : %p\n", sym->uctx);
998 }
999}
static int const char char buffer[256]
Definition acutest.h:578
int n
Definition acutest.h:579
#define fr_atexit_global_once(_init, _free, _uctx)
Definition atexit.h:211
static dl_loader_t * dl_loader
Definition fuzzer.c:44
static dl_t * dl
Definition fuzzer.c:43
static bool init
Definition fuzzer.c:42
#define RCSID(id)
Definition build.h:487
#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:383
int fr_get_lsan_state(void)
Definition debug.c:232
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
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:900
#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
void dl_loader_debug(FILE *fp, dl_loader_t *dl)
Called from a debugger to print information about a dl_loader.
Definition dl.c:977
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:858
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:783
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:965
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:828
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:700
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:744
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:803
#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:883
int dl_free(dl_t const *dl)
"free" a dl handle, possibly actually freeing it, and unloading the library
Definition dl.c:693
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
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:242
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
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:432
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:360
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:537
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
free(array)
talloc_free(hp)
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_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:824
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
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
#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:1459
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1592
#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:514
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:167
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
int talloc_decrease_ref_count(void const *ptr)
Decrease the reference count on a ptr.
Definition talloc.c:842
#define talloc_get_type_abort_const
Definition talloc.h:245
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:230
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#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_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1334