1 /* runtime/weak dynamic JACK linking
2  *
3  * (C) 2014 Robin Gareus <robin@gareus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "weak_libjack.h"
21 
22 #ifndef USE_WEAK_JACK
23 
have_libjack(void)24 int have_libjack (void) {
25 	return 0;
26 }
27 
28 #else
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <assert.h>
33 
34 #ifdef PLATFORM_WINDOWS
35 #include <windows.h>
36 #else
37 #include <dlfcn.h>
38 #endif
39 
lib_open(const char * const so)40 static void* lib_open(const char* const so) {
41 #ifdef PLATFORM_WINDOWS
42 	return (void*) LoadLibraryA(so);
43 #else
44 	return dlopen(so, RTLD_NOW|RTLD_LOCAL);
45 #endif
46 }
47 
lib_symbol(void * const lib,const char * const sym)48 static void* lib_symbol(void* const lib, const char* const sym) {
49 #ifdef PLATFORM_WINDOWS
50 	return (void*) GetProcAddress((HMODULE)lib, sym);
51 #else
52 	return dlsym(lib, sym);
53 #endif
54 }
55 
56 #if defined _MSC_VER  && ! defined __INTEL_COMPILER
57 typedef void * pvoid_t;
58 #define MAPSYM(SYM, FAIL) _j._ ## SYM = (func_t)lib_symbol(lib, "jack_" # SYM); \
59 	if (!_j._ ## SYM) err |= FAIL;
60 #elif defined NDEBUG
61 typedef void * __attribute__ ((__may_alias__)) pvoid_t;
62 #define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
63 	if (!_j._ ## SYM) err |= FAIL;
64 #else
65 typedef void * __attribute__ ((__may_alias__)) pvoid_t;
66 #define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
67 	if (!_j._ ## SYM) { \
68 		if (FAIL) { \
69 			fprintf(stderr, "*** WEAK-JACK: required symbol 'jack_%s' was not found\n", "" # SYM); \
70 		} \
71 		err |= FAIL; \
72 	}
73 #endif
74 
75 typedef void (* func_t) (void);
76 
77 /* function pointers to the real jack API */
78 static struct WeakJack {
79 	func_t _client_open; // special case due to varargs
80 
81 #define JCFUN(ERR, RTYPE, NAME, RVAL)              func_t _ ## NAME ;
82 #define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL)   func_t _ ## NAME ;
83 #define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE)   func_t _ ## NAME ;
84 #define JVFUN(ERR, NAME, DEF, ARGS, CODE)          func_t _ ## NAME ;
85 
86 #include "weak_libjack.def"
87 
88 #undef JCFUN
89 #undef JPFUN
90 #undef JXFUN
91 #undef JVFUN
92 } _j;
93 
94 static int _status = -1;
95 #if !defined _MSC_VER || defined __INTEL_COMPILER
96 __attribute__((constructor))
97 #endif
init_weak_jack(void)98 static void init_weak_jack(void)
99 {
100 	void* lib;
101 	int err = 0;
102 #ifndef NDEBUG
103 	fprintf(stderr, "*** WEAK-JACK: initializing\n");
104 #endif
105 
106 	memset(&_j, 0, sizeof(_j));
107 
108 #ifdef __APPLE__
109 	lib = lib_open("libjack.dylib");
110 	if (!lib) {
111 		lib = lib_open("/usr/local/lib/libjack.dylib");
112 	}
113 	if (!lib) {
114 		/* New Homebrew location */
115 		lib = lib_open("/opt/homebrew/lib/libjack.dylib");
116 		if (lib) {
117 			fprintf(stderr, "*** WEAK-JACK: using Homebrew\n");
118 		}
119 	}
120 	if (!lib) {
121 		/* MacPorts location */
122 		lib = lib_open("/opt/local/lib/libjack.dylib");
123 		if (lib) {
124 			fprintf(stderr, "*** WEAK-JACK: using MacPorts\n");
125 		}
126 	}
127 
128 #elif (defined PLATFORM_WINDOWS)
129 # if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
130 	lib = lib_open("libjack64.dll");
131 # else
132 	lib = lib_open("libjack.dll");
133 # endif
134 #else
135 	lib = lib_open("libjack.so.0");
136 #endif
137 	if (!lib) {
138 #ifndef NDEBUG
139 		fprintf(stderr, "*** WEAK-JACK: libjack was not found\n");
140 #endif
141 		_status = -2;
142 		return;
143 	}
144 
145 	/* found library, now lookup functions */
146 	MAPSYM(client_open, 2)
147 
148 #define JCFUN(ERR, RTYPE, NAME, RVAL)             MAPSYM(NAME, ERR)
149 #define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL)  MAPSYM(NAME, ERR)
150 #define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE)  MAPSYM(NAME, ERR)
151 #define JVFUN(ERR, NAME, DEF, ARGS, CODE)         MAPSYM(NAME, ERR)
152 
153 #include "weak_libjack.def"
154 
155 #undef JCFUN
156 #undef JPFUN
157 #undef JXFUN
158 #undef JVFUN
159 
160 	/* if a required symbol is not found, disable JACK completly */
161 	if (err) {
162 		_j._client_open = NULL;
163 	}
164 	_status = err;
165 #ifndef NDEBUG
166 	fprintf(stderr, "*** WEAK-JACK: %s. (%d)\n", err ? "jack is not available" : "OK", _status);
167 #endif
168 }
169 
have_libjack(void)170 int have_libjack (void) {
171 	if (_status == -1) {
172 		init_weak_jack();
173 	}
174 	return _status;
175 }
176 
177 /*******************************************************************************
178  * helper macros
179  */
180 
181 #if defined(__GNUC__) && (__GNUC__ > 2) && !defined(NDEBUG)
182 #define likely(expr) (__builtin_expect (!!(expr), 1))
183 #else
184 #define likely(expr) (expr)
185 #endif
186 
187 #ifndef NDEBUG
188 # define WJACK_WARNING(NAME) \
189 	fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME);
190 #else
191 # define WJACK_WARNING(NAME) ;
192 #endif
193 
194 /******************************************************************************
195  * JACK API wrapper functions.
196  *
197  * if a function pointer is set in the static struct WeakJack _j,
198  * the function is called directly.
199  * Otherwise a dummy NOOP implementation is provided.
200  * The latter is mainly for compile-time warnings.
201  *
202  * If libjack is not found, jack_client_open() will fail.
203  * In that case the application should not call any other libjack
204  * functions. Hence a real implementation is not needed.
205  * (jack ringbuffer may be an exception for some apps)
206  */
207 
208 /* dedicated support for jack_client_open(,..) variable arg function macro */
WJACK_get_client_open(void)209 func_t WJACK_get_client_open(void) {
210 	if (_status == -1) {
211 		init_weak_jack();
212 	}
213 	return _j._client_open;
214 }
215 
216 /* callback to set status */
WJACK_no_client_open(const char * client_name,jack_options_t options,jack_status_t * status,...)217 jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) {
218 	WJACK_WARNING(client_open);
219 	if (status) { *status = JackFailure; }
220 	return NULL;
221 }
222 
223 /*******************************************************************************
224  * Macros to wrap jack API
225  */
226 
227 /* abstraction for jack_client functions
228  *  rtype jack_function_name (jack_client_t *client) { return rval; }
229  */
230 #define JCFUN(ERR, RTYPE, NAME, RVAL) \
231 	RTYPE WJACK_ ## NAME (jack_client_t *client) { \
232 		if likely(_j._ ## NAME) { \
233 			return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \
234 		} else { \
235 			WJACK_WARNING(NAME) \
236 			return RVAL; \
237 		} \
238 	}
239 
240 /* abstraction for NOOP functions with return value
241  *  rtype jack_function_name (ARGS) { return rval; }
242  */
243 #define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) \
244 	RTYPE WJACK_ ## NAME DEF { \
245 		if likely(_j._ ## NAME) { \
246 			return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
247 		} else { \
248 			WJACK_WARNING(NAME) \
249 			return RVAL; \
250 		} \
251 	}
252 
253 /* abstraction for functions that need custom code.
254  * e.g. functions with return-value-pointer args,
255  * use CODE to initialize value
256  *
257  *  rtype jack_function_name (ARGS) { CODE }
258  */
259 #define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) \
260 	RTYPE WJACK_ ## NAME DEF { \
261 		if likely(_j._ ## NAME) { \
262 			return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
263 		} else { \
264 			WJACK_WARNING(NAME) \
265 			CODE \
266 		} \
267 	}
268 
269 /* abstraction for void functions with return-value-pointer args
270  *  void jack_function_name (ARGS) { CODE }
271  */
272 #define JVFUN(ERR, NAME, DEF, ARGS, CODE) \
273 	void WJACK_ ## NAME DEF { \
274 		if likely(_j._ ## NAME) { \
275 			((void (*)DEF) _j._ ## NAME) ARGS; \
276 		} else { \
277 			WJACK_WARNING(NAME) \
278 			CODE \
279 		} \
280 	}
281 
282 #include "weak_libjack.def"
283 
284 #undef JCFUN
285 #undef JPFUN
286 #undef JXFUN
287 #undef JVFUN
288 
289 #endif // end USE_WEAK_JACK
290