1 /*----------------------------------------------------------------------------
2 ChucK Concurrent, On-the-fly Audio Programming Language
3 Compiler and Virtual Machine
5 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
6 http://chuck.cs.princeton.edu/
7 http://soundlab.cs.princeton.edu/
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
27 // desc: chuck dynamic linking
29 // authors: Ge Wang (gewang@cs.princeton.edu)
30 // Perry R. Cook (prc@cs.princeton.edu)
31 // mac code from apple
34 //-----------------------------------------------------------------------------
36 #ifndef __CKDL_NO_BBQ__
37 #include "chuck_bbq.h"
43 //-----------------------------------------------------------------------------
45 // desc: load dynamic link library
46 //-----------------------------------------------------------------------------
47 t_CKBOOL
Chuck_DLL::load( const char * filename
, const char * func
, t_CKBOOL lazy
)
50 m_handle
= dlopen( filename
, lazy
? RTLD_LAZY
: RTLD_NOW
);
55 m_last_error
= dlerror();
60 m_filename
= filename
;
65 if( !lazy
&& !this->query() )
74 //-----------------------------------------------------------------------------
76 // desc: load dynamic link library
77 //-----------------------------------------------------------------------------
78 t_CKBOOL
Chuck_DLL::load( f_ck_query query_func
, t_CKBOOL lazy
)
80 m_query_func
= query_func
;
85 if( !lazy
&& !this->query() )
94 //-----------------------------------------------------------------------------
97 //-----------------------------------------------------------------------------
98 t_CKBOOL
Chuck_DLL::good() const
100 return ( m_handle
!= NULL
|| m_query_func
!= NULL
);
106 //-----------------------------------------------------------------------------
109 //-----------------------------------------------------------------------------
110 const Chuck_DL_Query
* Chuck_DLL::query( )
112 if( !m_handle
&& !m_query_func
)
114 m_last_error
= "dynamic link library not loaded for query...";
118 // return if there already
122 // get the address of the query function from the DLL
124 m_query_func
= (f_ck_query
)this->get_addr( m_func
.c_str() );
126 m_query_func
= (f_ck_query
)this->get_addr( (string("_")+m_func
).c_str() );
129 m_last_error
= string("no query function found in dll '")
130 + m_filename
+ string("'");
134 // get the address of the attach function from the DLL
136 m_attach_func
= (f_ck_attach
)this->get_addr( "ck_attach" );
138 m_attach_func
= (f_ck_attach
)this->get_addr( "_ck_attach" );
140 // get the address of the detach function from the DLL
142 m_detach_func
= (f_ck_detach
)this->get_addr( "ck_detach" );
144 m_detach_func
= (f_ck_detach
)this->get_addr( "_ck_detach" );
148 if( !m_query_func( &m_query
) )
150 m_last_error
= string("unsuccessful query in dll '") + m_filename
155 // load the proto table
156 Chuck_DL_Proto
* proto
;
157 m_name2proto
.clear();
158 for( t_CKUINT i
= 0; i
< m_query
.dll_exports
.size(); i
++ )
160 proto
= &m_query
.dll_exports
[i
];
161 if( m_name2proto
[proto
->name
] )
163 m_last_error
= string("duplicate export name '") + proto
->name
170 proto
->addr
= (f_ck_func
)this->get_addr( (char *)proto
->name
.c_str() );
173 m_last_error
= string("no addr associated with export '") +
174 proto
->name
+ string("'");
178 // put in the lookup table
179 m_name2proto
[proto
->name
] = proto
;
182 // load the proto table
183 Chuck_UGen_Info
* info
;
185 for( t_CKUINT j
= 0; j
< m_query
.ugen_exports
.size(); j
++ )
187 info
= &m_query
.ugen_exports
[j
];
188 if( m_name2ugen
[info
->name
] )
190 m_last_error
= string("duplicate export ugen name '") + info
->name
195 // put in the lookup table
196 m_name2ugen
[info
->name
] = info
;
202 if( m_attach_func
) m_attach_func( 0, NULL
);
210 //-----------------------------------------------------------------------------
213 //-----------------------------------------------------------------------------
214 t_CKBOOL
Chuck_DLL::unload()
216 if( !m_handle
&& !m_query_func
)
218 m_last_error
= "cannot unload dynamic library - nothing open...";
222 if( m_detach_func
) m_detach_func( 0, NULL
);
238 //-----------------------------------------------------------------------------
241 //-----------------------------------------------------------------------------
242 void * Chuck_DLL::get_addr( const char * symbol
)
246 m_last_error
= "cannot find addr from dynamic library - nothing open...";
250 return dlsym( m_handle
, symbol
);
256 //-----------------------------------------------------------------------------
259 //-----------------------------------------------------------------------------
260 const Chuck_DL_Proto
* Chuck_DLL::proto( const char * symbol
)
262 if( !m_handle
&& !m_query_func
)
264 m_last_error
= "cannot find proto from dynamic library - nothing open...";
268 // get the proto from the hash
269 Chuck_DL_Proto
* proto
= m_name2proto
[symbol
];
272 m_last_error
= string("no prototype function '") + string(symbol
)
283 //-----------------------------------------------------------------------------
284 // name: last_error()
286 //-----------------------------------------------------------------------------
287 const char * Chuck_DLL::last_error() const
289 return m_last_error
.c_str();
295 //-----------------------------------------------------------------------------
296 // name: Chuck_DL_Query
298 //-----------------------------------------------------------------------------
299 Chuck_DL_Query::Chuck_DL_Query( )
300 { add_export
= __ck_addexport
; add_param
= __ck_addparam
;
301 ugen_add
= __ck_ugen_add
; ugen_func
= __ck_ugen_func
;
302 ugen_ctrl
= __ck_ugen_ctrl
; set_name
= __ck_setname
;
303 ugen_extends
= __ck_ugen_extends
; // XXX - pld
304 dll_name
= "[noname]"; reserved
= NULL
;
306 #ifndef __CKDL_NO_BBQ__
307 srate
= Digitalio::sampling_rate() ; bufsize
= Digitalio::buffer_size();
309 srate
= 0; bufsize
= 0;
312 ugen_exports
.resize( 128 );
320 extern "C" void CK_DLL_CALL
__ck_addexport( Chuck_DL_Query
* query
,
321 const char * type
, const char * name
, f_ck_func addr
,
323 { query
->dll_exports
.push_back( Chuck_DL_Proto( type
, name
, (void *)addr
, is_func
) ); }
326 extern "C" void CK_DLL_CALL
__ck_addparam( Chuck_DL_Query
* query
,
327 const char * type
, const char * name
)
328 { if( query
->dll_exports
.size() )
329 query
->dll_exports
[query
->dll_exports
.size()-1].add_param( type
, name
); }
332 extern "C" void CK_DLL_CALL
__ck_ugen_add( Chuck_DL_Query
* query
,
333 const char * name
, void * reserved
)
334 { query
->ugen_exports
.push_back( Chuck_UGen_Info( name
) );
335 query
->reserved
= reserved
; }
338 extern "C" void CK_DLL_CALL
__ck_ugen_func( Chuck_DL_Query
* query
,
339 f_ctor c
, f_dtor d
, f_tick t
, f_pmsg p
)
340 { if( query
->ugen_exports
.size() )
341 query
->ugen_exports
[query
->ugen_exports
.size()-1].set( c
, d
, t
, p
); }
344 extern "C" void CK_DLL_CALL
__ck_ugen_ctrl( Chuck_DL_Query
* query
,
345 f_ctrl c
, f_cget g
, const char * t
, const char * n
)
346 { if( query
->ugen_exports
.size() )
347 query
->ugen_exports
[query
->ugen_exports
.size()-1].add( c
, g
, t
, n
); }
349 //XXX - pld inherit functions from 'parent' ugen
350 extern "C" void CK_DLL_CALL
__ck_ugen_extends( Chuck_DL_Query
* query
,
351 const char * parent
)
353 if( query
->ugen_exports
.size() > 1 )
354 for( int i
= 0 ; i
< query
->ugen_exports
.size() - 1 ; i
++ )
355 if( strcmp ( parent
, query
->ugen_exports
[i
].name
.c_str() ) == 0 )
357 query
->ugen_exports
[query
->ugen_exports
.size()-1].inherit(
358 &(query
->ugen_exports
[i
]) );
365 extern "C" void CK_DLL_CALL
__ck_setname( Chuck_DL_Query
* query
,
367 { query
->dll_name
= name
; }
370 #if defined(__WINDOWS_DS__)
375 void *dlopen( const char *path
, int mode
)
377 return (void *)LoadLibrary(path
);
380 int dlclose( void *handle
)
382 FreeLibrary((HMODULE
)handle
);
386 void *dlsym( void * handle
, const char *symbol
)
388 return (void *)GetProcAddress((HMODULE
)handle
, symbol
);
391 const char * dlerror( void )
393 int error
= GetLastError();
394 if( error
== 0 ) return NULL
;
395 sprintf( dlerror_buffer
, "%i", error
);
396 return dlerror_buffer
;
402 #if defined(__MACOSX_CORE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1030
411 #include <sys/types.h>
412 #include <sys/stat.h>
414 #include "mach-o/dyld.h"
420 #define DEBUG_PRINT(format) fprintf(stderr,(format));fflush(stderr)
421 #define DEBUG_PRINT1(format,arg1) fprintf(stderr,(format),(arg1));\
423 #define DEBUG_PRINT2(format,arg1,arg2) fprintf(stderr,(format),\
424 (arg1),(arg2));fflush(stderr)
425 #define DEBUG_PRINT3(format,arg1,arg2,arg3) fprintf(stderr,(format),\
426 (arg1),(arg2),(arg3));fflush(stderr)
428 #define DEBUG_PRINT(format) /**/
429 #define DEBUG_PRINT1(format,arg1) /**/
430 #define DEBUG_PRINT2(format,arg1,arg2) /**/
431 #define DEBUG_PRINT3(format,arg1,arg2,arg3) /**/
436 * The structure of a dlopen() handle.
438 struct dlopen_handle
{
439 dev_t dev
; /* the path's device and inode number from stat(2) */
441 int dlopen_mode
; /* current dlopen mode for this handle */
442 int dlopen_count
; /* number of times dlopen() called on this handle */
443 NSModule module
; /* the NSModule returned by NSLinkModule() */
444 struct dlopen_handle
*prev
;
445 struct dlopen_handle
*next
;
447 static struct dlopen_handle
*dlopen_handles
= NULL
;
448 static const struct dlopen_handle main_program_handle
= {NULL
};
449 static char *dlerror_pointer
= NULL
;
452 * NSMakePrivateModulePublic() is not part of the public dyld API so we define
453 * it here. The internal dyld function pointer for
454 * __dyld_NSMakePrivateModulePublic is returned so thats all that maters to get
455 * the functionality need to implement the dlopen() interfaces.
459 NSMakePrivateModulePublic(
462 static int (*p
)(NSModule module
) = NULL
;
465 _dyld_func_lookup("__dyld_NSMakePrivateModulePublic",
466 (unsigned long *)&p
);
469 printf("_dyld_func_lookup of __dyld_NSMakePrivateModulePublic "
478 * helper routine: search for a named module in various locations
483 const char *filename
,
485 struct stat
*stat_buf
)
487 const char *pathspec
;
492 const char *envvars
[] = {
493 "$DYLD_LIBRARY_PATH",
499 pathbuf_end
= pathbuf
+ PATH_MAX
- 8;
501 for(envvar_index
= 0; envvars
[envvar_index
]; envvar_index
++){
502 if(envvars
[envvar_index
][0] == '$'){
503 pathspec
= getenv(envvars
[envvar_index
]+1);
506 pathspec
= envvars
[envvar_index
];
509 if(pathspec
!= NULL
){
512 /* extract path list element */
515 while(*p
&& *p
!= ':' && q
< pathbuf_end
) *q
++ = *p
++;
516 if(q
== pathbuf
){ /* empty element */
527 element
= p
; /* this terminates the loop */
530 /* add slash if neccessary */
531 if(*(q
-1) != '/' && q
< pathbuf_end
){
535 /* append module name */
537 while(*p
&& q
< pathbuf_end
) *q
++ = *p
++;
540 if(q
>= pathbuf_end
){
541 /* maybe add an error message here */
545 if(stat(pathbuf
, stat_buf
) == 0){
552 /* we have searched everywhere, now we give up */
557 * dlopen() the MacOS X version of the FreeBSD dlopen() interface.
564 const char *module_path
;
566 struct stat stat_buf
;
567 NSObjectFileImage objectFileImage
;
568 NSObjectFileImageReturnCode ofile_result_code
;
570 struct dlopen_handle
*p
;
571 unsigned long options
;
574 char pathbuf
[PATH_MAX
];
576 DEBUG_PRINT2("libdl: dlopen(%s,0x%x) -> ", path
, (unsigned int)mode
);
578 dlerror_pointer
= NULL
;
580 * A NULL path is to indicate the caller wants a handle for the
584 retval
= (void *)&main_program_handle
;
585 DEBUG_PRINT1("main / %p\n", retval
);
589 /* see if the path exists and if so get the device and inode number */
590 if(stat(path
, &stat_buf
) == -1){
591 dlerror_pointer
= strerror(errno
);
594 DEBUG_PRINT1("ERROR (stat): %s\n", dlerror_pointer
);
598 /* search for the module in various places */
599 if(_dl_search_paths(path
, pathbuf
, &stat_buf
)){
600 /* dlerror_pointer is unmodified */
601 DEBUG_PRINT1("ERROR (stat): %s\n", dlerror_pointer
);
604 DEBUG_PRINT1("found %s -> ", pathbuf
);
605 module_path
= pathbuf
;
606 dlerror_pointer
= NULL
;
613 * If we don't want an unshared handle see if we already have a handle
616 if((mode
& RTLD_UNSHARED
) != RTLD_UNSHARED
){
619 if(p
->dev
== stat_buf
.st_dev
&& p
->ino
== stat_buf
.st_ino
){
620 /* skip unshared handles */
621 if((p
->dlopen_mode
& RTLD_UNSHARED
) == RTLD_UNSHARED
)
624 * We have already created a handle for this path. The
625 * caller might be trying to promote an RTLD_LOCAL handle
626 * to a RTLD_GLOBAL. Or just looking it up with
629 if((p
->dlopen_mode
& RTLD_LOCAL
) == RTLD_LOCAL
&&
630 (mode
& RTLD_GLOBAL
) == RTLD_GLOBAL
){
631 /* promote the handle */
632 if(NSMakePrivateModulePublic(p
->module
) == TRUE
){
633 p
->dlopen_mode
&= ~RTLD_LOCAL
;
634 p
->dlopen_mode
|= RTLD_GLOBAL
;
636 DEBUG_PRINT1("%p\n", p
);
640 dlerror_pointer
= "can't promote handle from "
641 "RTLD_LOCAL to RTLD_GLOBAL";
642 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
647 DEBUG_PRINT1("%p\n", p
);
655 * We do not have a handle for this path if we were just trying to
656 * look it up return NULL to indicate we don't have it.
658 if((mode
& RTLD_NOLOAD
) == RTLD_NOLOAD
){
659 dlerror_pointer
= "no existing handle for path RTLD_NOLOAD test";
660 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
664 /* try to create an object file image from this path */
665 ofile_result_code
= NSCreateObjectFileImageFromFile(module_path
,
667 if(ofile_result_code
!= NSObjectFileImageSuccess
){
668 switch(ofile_result_code
){
669 case NSObjectFileImageFailure
:
670 dlerror_pointer
= "object file setup failure";
671 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
673 case NSObjectFileImageInappropriateFile
:
674 dlerror_pointer
= "not a Mach-O MH_BUNDLE file type";
675 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
677 case NSObjectFileImageArch
:
678 dlerror_pointer
= "no object for this architecture";
679 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
681 case NSObjectFileImageFormat
:
682 dlerror_pointer
= "bad object file format";
683 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
685 case NSObjectFileImageAccess
:
686 dlerror_pointer
= "can't read object file";
687 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
690 dlerror_pointer
= "unknown error from "
691 "NSCreateObjectFileImageFromFile()";
692 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
697 /* try to link in this object file image */
698 options
= NSLINKMODULE_OPTION_PRIVATE
;
699 if((mode
& RTLD_NOW
) == RTLD_NOW
)
700 options
|= NSLINKMODULE_OPTION_BINDNOW
;
701 module
= NSLinkModule(objectFileImage
, module_path
, options
);
702 NSDestroyObjectFileImage(objectFileImage
) ;
704 dlerror_pointer
= "NSLinkModule() failed for dlopen()";
705 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
710 * If the handle is to be global promote the handle. It is done this
711 * way to avoid multiply defined symbols.
713 if((mode
& RTLD_GLOBAL
) == RTLD_GLOBAL
){
714 if(NSMakePrivateModulePublic(module
) == FALSE
){
715 dlerror_pointer
= "can't promote handle from RTLD_LOCAL to "
717 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
722 p
= (dlopen_handle
*)malloc(sizeof(struct dlopen_handle
));
724 dlerror_pointer
= "can't allocate memory for the dlopen handle";
725 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
729 /* fill in the handle */
730 p
->dev
= stat_buf
.st_dev
;
731 p
->ino
= stat_buf
.st_ino
;
732 if(mode
& RTLD_GLOBAL
)
733 p
->dlopen_mode
= RTLD_GLOBAL
;
735 p
->dlopen_mode
= RTLD_LOCAL
;
736 p
->dlopen_mode
|= (mode
& RTLD_UNSHARED
) |
737 (mode
& RTLD_NODELETE
) |
738 (mode
& RTLD_LAZY_UNDEF
);
742 p
->next
= dlopen_handles
;
743 if(dlopen_handles
!= NULL
)
744 dlopen_handles
->prev
= p
;
747 /* call the init function if one exists */
748 NSSymbol
= NSLookupSymbolInModule(p
->module
, "__init");
749 if(NSSymbol
!= NULL
){
750 init
= ( void(*)() )NSAddressOfSymbol(NSSymbol
);
754 DEBUG_PRINT1("%p\n", p
);
759 * dlsym() the MacOS X version of the FreeBSD dlopen() interface.
766 struct dlopen_handle
*dlopen_handle
, *p
;
770 DEBUG_PRINT2("libdl: dlsym(%p,%s) -> ", handle
, symbol
);
772 dlopen_handle
= (struct dlopen_handle
*)handle
;
775 * If this is the handle for the main program do a global lookup.
777 if(dlopen_handle
== (struct dlopen_handle
*)&main_program_handle
){
778 if(NSIsSymbolNameDefined(symbol
) == TRUE
){
779 NSSymbol
= NSLookupAndBindSymbol(symbol
);
780 address
= NSAddressOfSymbol(NSSymbol
);
781 dlerror_pointer
= NULL
;
782 DEBUG_PRINT1("%p\n", address
);
786 dlerror_pointer
= "symbol not found";
787 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
793 * Find this handle and do a lookup in just this module.
797 if(dlopen_handle
== p
){
798 NSSymbol
= NSLookupSymbolInModule(p
->module
, symbol
);
799 if(NSSymbol
!= NULL
){
800 address
= NSAddressOfSymbol(NSSymbol
);
801 dlerror_pointer
= NULL
;
802 DEBUG_PRINT1("%p\n", address
);
806 dlerror_pointer
= "symbol not found";
807 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
814 dlerror_pointer
= "bad handle passed to dlsym()";
815 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
820 * dlerror() the MacOS X version of the FreeBSD dlopen() interface.
828 p
= (const char *)dlerror_pointer
;
829 dlerror_pointer
= NULL
;
834 * dlclose() the MacOS X version of the FreeBSD dlopen() interface.
840 struct dlopen_handle
*p
, *q
;
841 unsigned long options
;
845 DEBUG_PRINT1("libdl: dlclose(%p) -> ", handle
);
847 dlerror_pointer
= NULL
;
848 q
= (struct dlopen_handle
*)handle
;
852 /* if the dlopen() count is not zero we are done */
854 if(p
->dlopen_count
!= 0){
859 /* call the fini function if one exists */
860 NSSymbol
= NSLookupSymbolInModule(p
->module
, "__fini");
861 if(NSSymbol
!= NULL
){
862 fini
= ( void(*)() )NSAddressOfSymbol(NSSymbol
);
866 /* unlink the module for this handle */
868 if(p
->dlopen_mode
& RTLD_NODELETE
)
869 options
|= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED
;
870 if(p
->dlopen_mode
& RTLD_LAZY_UNDEF
)
871 options
|= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES
;
872 if(NSUnLinkModule(p
->module
, options
) == FALSE
){
873 dlerror_pointer
= "NSUnLinkModule() failed for dlclose()";
874 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
878 p
->prev
->next
= p
->next
;
880 p
->next
->prev
= p
->prev
;
881 if(dlopen_handles
== p
)
882 dlopen_handles
= p
->next
;
889 dlerror_pointer
= "invalid handle passed to dlclose()";
890 DEBUG_PRINT1("ERROR: %s\n", dlerror_pointer
);
898 // do nothing, it's all in dlfcn