1/***
2  This file is part of PulseAudio.
3
4  Copyright 2004-2008 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#ifdef HAVE_DLFCN_H
26#include <dlfcn.h>
27#endif
28
29#ifdef HAVE_SYS_DL_H
30#include <sys/dl.h>
31#endif
32
33#include <string.h>
34
35#include <ltdl.h>
36
37#include <pulsecore/i18n.h>
38#include <pulsecore/macro.h>
39#include <pulsecore/log.h>
40
41#include "ltdl-bind-now.h"
42
43#ifdef RTLD_NOW
44#define PA_BIND_NOW RTLD_NOW
45#elif defined(DL_NOW)
46#define PA_BIND_NOW DL_NOW
47#else
48#undef PA_BIND_NOW
49#endif
50
51#ifdef OS_IS_WIN32
52#undef PA_BIND_NOW
53#endif
54
55#ifdef PA_BIND_NOW
56
57/*
58  To avoid lazy relocations during runtime in our RT threads we add
59  our own shared object loader with uses RTLD_NOW if it is
60  available. The standard ltdl loader prefers RTLD_LAZY.
61
62  Please note that this loader doesn't have any influence on
63  relocations on any libraries that are already loaded into our
64  process, i.e. because the pulseaudio binary links directly to
65  them. To disable lazy relocations for those libraries it is possible
66  to set $LT_BIND_NOW before starting the pulseaudio binary.
67*/
68
69static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
70    lt_module m;
71
72    pa_assert(fname);
73
74    if (!(m = dlopen(fname, PA_BIND_NOW))) {
75        pa_log(_("Failed to open module %s: %s"), fname, dlerror());
76        lt_dlseterror(LT_ERROR_CANNOT_OPEN);
77        return NULL;
78    }
79
80    return m;
81}
82
83static int bind_now_close(lt_user_data d, lt_module m) {
84
85    pa_assert(m);
86
87    if (dlclose(m) != 0) {
88        lt_dlseterror(LT_ERROR_CANNOT_CLOSE);
89        return 1;
90    }
91
92    return 0;
93}
94
95static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol) {
96    lt_ptr ptr;
97
98    pa_assert(m);
99    pa_assert(symbol);
100
101    if (!(ptr = dlsym(m, symbol))) {
102        lt_dlseterror(LT_ERROR_SYMBOL_NOT_FOUND);
103        return NULL;
104    }
105
106    return ptr;
107}
108
109static lt_dlvtable *bindnow_loader = NULL;
110#endif
111
112void pa_ltdl_init(void) {
113
114#ifdef PA_BIND_NOW
115    const lt_dlvtable *dlopen_loader;
116#endif
117
118    pa_assert_se(lt_dlinit() == 0);
119
120#ifdef PA_BIND_NOW
121    /* Already initialised */
122    if (bindnow_loader)
123        return;
124
125    if (!(dlopen_loader = lt_dlloader_find((char*) "lt_dlopen"))) {
126        pa_log_warn(_("Failed to find original lt_dlopen loader."));
127        return;
128    }
129
130    if (!(bindnow_loader = malloc(sizeof(lt_dlvtable)))) {
131        pa_log_error(_("Failed to allocate new dl loader."));
132        return;
133    }
134
135    memcpy(bindnow_loader, dlopen_loader, sizeof(*bindnow_loader));
136    bindnow_loader->name = "bind-now-loader";
137    bindnow_loader->module_open = bind_now_open;
138    bindnow_loader->module_close = bind_now_close;
139    bindnow_loader->find_sym = bind_now_find_sym;
140    bindnow_loader->priority = LT_DLLOADER_PREPEND;
141
142    /* Add our BIND_NOW loader as the default module loader. */
143    if (lt_dlloader_add(bindnow_loader) != 0) {
144        pa_log_warn(_("Failed to add bind-now-loader."));
145        free(bindnow_loader);
146        bindnow_loader = NULL;
147    }
148#endif
149}
150
151void pa_ltdl_done(void) {
152    pa_assert_se(lt_dlexit() == 0);
153
154#ifdef PA_BIND_NOW
155    /* lt_dlexit() will free our loader vtable, hence reset our
156     * pointer to it here */
157    bindnow_loader = NULL;
158#endif
159}
160