1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21
22 #include "SC_Lib_Cintf.h"
23 #include "SC_CoreAudio.h"
24 #include "SC_UnitDef.h"
25 #include "SC_BufGen.h"
26 #include "SC_World.h"
27 #include "SC_WorldOptions.h"
28 #include "SC_StringParser.h"
29 #include "SC_InterfaceTable.h"
30 #include "SC_Filesystem.hpp"
31 #include "ErrorMessage.hpp" // for apiVersionMismatch, apiVersionMissing
32 #include <stdexcept>
33 #include <iostream>
34
35 #ifndef _MSC_VER
36 # include <dirent.h>
37 #endif // _MSC_VER
38
39 #ifdef _WIN32
40 # include "SC_Win32Utils.h"
41 #else
42 # include <dlfcn.h>
43 # include <libgen.h>
44 # include <sys/param.h>
45 #endif // _WIN32
46
47 #include <boost/filesystem/path.hpp> // path
48 #include <boost/filesystem/operations.hpp> // is_directory
49
50 #ifdef __APPLE__
51 extern "C" {
52 # include <mach-o/dyld.h>
53 # include <mach-o/getsect.h>
54 }
55 char gTempVal;
56 #endif // __APPLE__
57
58 namespace bfs = boost::filesystem;
59
60 Malloc gMalloc;
61 HashTable<SC_LibCmd, Malloc>* gCmdLib;
62 HashTable<struct UnitDef, Malloc>* gUnitDefLib = nullptr;
63 HashTable<struct BufGen, Malloc>* gBufGenLib = nullptr;
64 HashTable<struct PlugInCmd, Malloc>* gPlugInCmds = nullptr;
65 extern struct InterfaceTable gInterfaceTable;
66 SC_LibCmd* gCmdArray[NUMBER_OF_COMMANDS];
67
68 void initMiscCommands();
69 static bool PlugIn_LoadDir(const bfs::path& dir, bool reportError);
70 std::vector<void*> open_handles;
71 #ifdef __APPLE__
read_section(const struct mach_header * mhp,unsigned long slide,const char * segname,const char * sectname)72 void read_section(const struct mach_header* mhp, unsigned long slide, const char* segname, const char* sectname) {
73 u_int32_t size;
74 char* sect = getsectdatafromheader(mhp, segname, sectname, &size);
75 if (!sect)
76 return;
77
78 char* start = sect + slide;
79 char* end = start + size;
80
81 while (start != end) {
82 gTempVal += *(char*)start;
83 start++;
84 }
85 }
86 #endif
87
88 extern void IO_Load(InterfaceTable* table);
89 extern void Osc_Load(InterfaceTable* table);
90 extern void Delay_Load(InterfaceTable* table);
91 extern void BinaryOp_Load(InterfaceTable* table);
92 extern void Filter_Load(InterfaceTable* table);
93 extern void Gendyn_Load(InterfaceTable* table);
94 extern void LF_Load(InterfaceTable* table);
95 extern void Noise_Load(InterfaceTable* table);
96 extern void MulAdd_Load(InterfaceTable* table);
97 extern void Grain_Load(InterfaceTable* table);
98 extern void Pan_Load(InterfaceTable* table);
99 extern void Reverb_Load(InterfaceTable* table);
100 extern void Trigger_Load(InterfaceTable* table);
101 extern void UnaryOp_Load(InterfaceTable* table);
102 extern void DiskIO_Load(InterfaceTable* table);
103 extern void Test_Load(InterfaceTable* table);
104 extern void PhysicalModeling_Load(InterfaceTable* table);
105 extern void Demand_Load(InterfaceTable* table);
106 extern void DynNoise_Load(InterfaceTable* table);
107 extern void FFT_UGens_Load(InterfaceTable* table);
108 extern void iPhone_Load(InterfaceTable* table);
109
110
deinitialize_library()111 void deinitialize_library() {
112 #ifdef _WIN32
113 for (void* ptrhinstance : open_handles) {
114 HINSTANCE hinstance = (HINSTANCE)ptrhinstance;
115 void* ptr = (void*)GetProcAddress(hinstance, "unload");
116 if (ptr) {
117 UnLoadPlugInFunc unloadFunc = (UnLoadPlugInFunc)ptr;
118 (*unloadFunc)();
119 }
120 }
121 // FreeLibrary dlclose(handle);
122 #else
123 for (void* handle : open_handles) {
124 void* ptr = dlsym(handle, "unload");
125 if (ptr) {
126 UnLoadPlugInFunc unloadFunc = (UnLoadPlugInFunc)ptr;
127 (*unloadFunc)();
128 }
129 }
130 #endif
131 open_handles.clear();
132 }
initialize_library(const char * uGensPluginPath)133 void initialize_library(const char* uGensPluginPath) {
134 gCmdLib = new HashTable<SC_LibCmd, Malloc>(&gMalloc, 64, true);
135 gUnitDefLib = new HashTable<UnitDef, Malloc>(&gMalloc, 512, true);
136 gBufGenLib = new HashTable<BufGen, Malloc>(&gMalloc, 512, true);
137 gPlugInCmds = new HashTable<PlugInCmd, Malloc>(&gMalloc, 64, true);
138
139 initMiscCommands();
140
141 #ifdef STATIC_PLUGINS
142 IO_Load(&gInterfaceTable);
143 Osc_Load(&gInterfaceTable);
144 Delay_Load(&gInterfaceTable);
145 BinaryOp_Load(&gInterfaceTable);
146 Filter_Load(&gInterfaceTable);
147 Gendyn_Load(&gInterfaceTable);
148 LF_Load(&gInterfaceTable);
149 Noise_Load(&gInterfaceTable);
150 MulAdd_Load(&gInterfaceTable);
151 Grain_Load(&gInterfaceTable);
152 Pan_Load(&gInterfaceTable);
153 Reverb_Load(&gInterfaceTable);
154 Trigger_Load(&gInterfaceTable);
155 UnaryOp_Load(&gInterfaceTable);
156 DiskIO_Load(&gInterfaceTable);
157 PhysicalModeling_Load(&gInterfaceTable);
158 Test_Load(&gInterfaceTable);
159 Demand_Load(&gInterfaceTable);
160 DynNoise_Load(&gInterfaceTable);
161 # if defined(SC_IPHONE) && !TARGET_IPHONE_SIMULATOR
162 iPhone_Load(&gInterfaceTable);
163 # endif
164 FFT_UGens_Load(&gInterfaceTable);
165 return;
166 #endif // STATIC_PLUGINS
167
168 // If uGensPluginPath is supplied, it is exclusive.
169 bool loadUGensExtDirs = true;
170 if (uGensPluginPath) {
171 loadUGensExtDirs = false;
172 SC_StringParser sp(uGensPluginPath, SC_STRPARSE_PATHDELIMITER);
173 while (!sp.AtEnd()) {
174 PlugIn_LoadDir(const_cast<char*>(sp.NextToken()), true);
175 }
176 }
177
178 using DirName = SC_Filesystem::DirName;
179
180 if (loadUGensExtDirs) {
181 #ifdef SC_PLUGIN_DIR
182 // load globally installed plugins
183 if (bfs::is_directory(SC_PLUGIN_DIR)) {
184 PlugIn_LoadDir(SC_PLUGIN_DIR, true);
185 }
186 #endif // SC_PLUGIN_DIR
187 // load default plugin directory
188 const bfs::path pluginDir = SC_Filesystem::instance().getDirectory(DirName::Resource) / SC_PLUGIN_DIR_NAME;
189
190 if (bfs::is_directory(pluginDir)) {
191 PlugIn_LoadDir(pluginDir, true);
192 }
193 }
194
195 // get extension directories
196 if (loadUGensExtDirs) {
197 // load system extension plugins
198 const bfs::path sysExtDir = SC_Filesystem::instance().getDirectory(DirName::SystemExtension);
199 PlugIn_LoadDir(sysExtDir, false);
200
201 // load user extension plugins
202 const bfs::path userExtDir = SC_Filesystem::instance().getDirectory(DirName::UserExtension);
203 PlugIn_LoadDir(userExtDir, false);
204
205 // load user plugin directories
206 SC_StringParser sp(getenv("SC_PLUGIN_PATH"), SC_STRPARSE_PATHDELIMITER);
207 while (!sp.AtEnd()) {
208 PlugIn_LoadDir(sp.NextToken(), true);
209 }
210 }
211 #ifdef __APPLE__
212 /* on darwin plugins are lazily loaded (dlopen uses mmap internally), which can produce audible
213 glitches when UGens have to be paged-in. to work around this we preload all the plugins by
214 iterating through their memory space. */
215
216 # ifndef __x86_64__
217 /* seems to cause a stack corruption on llvm-gcc-4.2, sdk 10.5 on 10.6 */
218
219 unsigned long images = _dyld_image_count();
220 for (unsigned long i = 0; i < images; i++) {
221 const mach_header* hdr = _dyld_get_image_header(i);
222 unsigned long slide = _dyld_get_image_vmaddr_slide(i);
223 const char* name = _dyld_get_image_name(i);
224 uint32_t size;
225 char* sect;
226
227 if (!strcmp(name + (strlen(name) - 4), ".scx")) {
228 read_section(hdr, slide, "__TEXT", "__text");
229 read_section(hdr, slide, "__TEXT", "__const");
230 read_section(hdr, slide, "__TEXT", "__cstring");
231 read_section(hdr, slide, "__TEXT", "__picsymbol_stub");
232 read_section(hdr, slide, "__TEXT", "__symbol_stub");
233 read_section(hdr, slide, "__TEXT", "__const");
234 read_section(hdr, slide, "__TEXT", "__literal4");
235 read_section(hdr, slide, "__TEXT", "__literal8");
236
237 read_section(hdr, slide, "__DATA", "__data");
238 read_section(hdr, slide, "__DATA", "__la_symbol_ptr");
239 read_section(hdr, slide, "__DATA", "__nl_symbol_ptr");
240 read_section(hdr, slide, "__DATA", "__dyld");
241 read_section(hdr, slide, "__DATA", "__const");
242 read_section(hdr, slide, "__DATA", "__mod_init_func");
243 read_section(hdr, slide, "__DATA", "__bss");
244 read_section(hdr, slide, "__DATA", "__common");
245
246 read_section(hdr, slide, "__IMPORT", "__jump_table");
247 read_section(hdr, slide, "__IMPORT", "__pointers");
248 }
249 }
250 # endif // __x86_64__
251
252 #endif // ifdef __APPLE__
253 }
254
255 typedef int (*InfoFunction)();
256
checkAPIVersion(void * f,const char * filename)257 bool checkAPIVersion(void* f, const char* filename) {
258 using namespace std;
259 using namespace scsynth;
260
261 if (f) {
262 InfoFunction fn = (InfoFunction)f;
263 int pluginVersion = (*fn)();
264 if (pluginVersion == sc_api_version)
265 return true;
266 else
267 cout << ErrorMessage::apiVersionMismatch(filename, sc_api_version, pluginVersion) << endl;
268 } else {
269 cout << ErrorMessage::apiVersionNotFound(filename) << endl;
270 }
271
272 return false;
273 }
274
checkServerVersion(void * f,const char * filename)275 bool checkServerVersion(void* f, const char* filename) {
276 if (f) {
277 InfoFunction fn = (InfoFunction)f;
278 if ((*fn)() == 1)
279 return false;
280 }
281 return true;
282 }
283
PlugIn_Load(const bfs::path & filename)284 static bool PlugIn_Load(const bfs::path& filename) {
285 #ifdef _WIN32
286 HINSTANCE hinstance = LoadLibraryW(filename.wstring().c_str());
287 // here, we have to use a utf-8 version of the string for printing
288 // because the native encoding on Windows is utf-16.
289 const std::string filename_utf8_str = SC_Codecvt::path_to_utf8_str(filename);
290 if (!hinstance) {
291 char* s;
292 DWORD lastErr = GetLastError();
293 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
294 lastErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&s, 0, NULL);
295 scprintf("*** ERROR: LoadLibrary '%s' err '%s'\n", filename_utf8_str.c_str(), s);
296 LocalFree(s);
297 return false;
298 }
299
300 void* apiVersionPtr = (void*)GetProcAddress(hinstance, "api_version");
301 if (!checkAPIVersion(apiVersionPtr, filename_utf8_str.c_str())) {
302 FreeLibrary(hinstance);
303 return false;
304 }
305
306 void* serverCheckPtr = (void*)GetProcAddress(hinstance, "server_type");
307 if (!checkServerVersion(serverCheckPtr, filename_utf8_str.c_str())) {
308 FreeLibrary(hinstance);
309 return false;
310 }
311
312 void* ptr = (void*)GetProcAddress(hinstance, "load");
313 if (!ptr) {
314 char* s;
315 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
316 GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&s, 0, NULL);
317 scprintf("*** ERROR: GetProcAddress err '%s'\n", s);
318 LocalFree(s);
319
320 FreeLibrary(hinstance);
321 return false;
322 }
323
324 LoadPlugInFunc loadFunc = (LoadPlugInFunc)ptr;
325 (*loadFunc)(&gInterfaceTable);
326
327 // FIXME: at the moment we never call FreeLibrary() on a loaded plugin
328 open_handles.push_back(hinstance);
329 return true;
330
331 #else // (ifndef _WIN32)
332 void* handle = dlopen(filename.c_str(), RTLD_NOW);
333
334 if (!handle) {
335 scprintf("*** ERROR: dlopen '%s' err '%s'\n", filename.c_str(), dlerror());
336 dlclose(handle);
337 return false;
338 }
339
340 void* apiVersionPtr = (void*)dlsym(handle, "api_version");
341 if (!checkAPIVersion(apiVersionPtr, filename.c_str())) {
342 dlclose(handle);
343 return false;
344 }
345
346 void* serverCheckPtr = (void*)dlsym(handle, "server_type");
347 if (!checkServerVersion(serverCheckPtr, filename.c_str())) {
348 dlclose(handle);
349 return false;
350 }
351
352 void* ptr = dlsym(handle, "load");
353 if (!ptr) {
354 scprintf("*** ERROR: dlsym load err '%s'\n", dlerror());
355 dlclose(handle);
356 return false;
357 }
358
359 LoadPlugInFunc loadFunc = (LoadPlugInFunc)ptr;
360 (*loadFunc)(&gInterfaceTable);
361
362 open_handles.push_back(handle);
363 return true;
364
365 #endif // _WIN32
366 }
367
PlugIn_LoadDir(const bfs::path & dir,bool reportError)368 static bool PlugIn_LoadDir(const bfs::path& dir, bool reportError) {
369 boost::system::error_code ec;
370 bfs::recursive_directory_iterator rditer(dir, bfs::symlink_option::recurse, ec);
371
372 if (ec) {
373 if (reportError) {
374 scprintf("*** ERROR: open directory failed '%s': %s\n", SC_Codecvt::path_to_utf8_str(dir).c_str(),
375 ec.message().c_str());
376 fflush(stdout);
377 }
378 return false;
379 }
380
381 while (rditer != bfs::end(rditer)) {
382 const bfs::path path = *rditer;
383
384 if (bfs::is_directory(path)) {
385 if (SC_Filesystem::instance().shouldNotCompileDirectory(path))
386 rditer.no_push();
387 else
388 ; // do nothing; recursion for free
389 } else if (path.extension() == SC_PLUGIN_EXT) {
390 // don't need to check result: PlugIn_Load does its own error handling and printing.
391 // A `false` return value here just means that loading didn't occur, which is not
392 // an error condition for us.
393 PlugIn_Load(path);
394 } else {
395 // not a plugin, do nothing
396 }
397
398 rditer.increment(ec);
399 if (ec) {
400 scprintf("*** ERROR: Could not iterate on directory '%s': %s\n", SC_Codecvt::path_to_utf8_str(path).c_str(),
401 ec.message().c_str());
402 return false;
403 }
404 }
405
406 return true;
407 }
408