
/* Support for dynamic loading of extension modules */

#include "Python.h"
#include "importdl.h"


#ifdef WITH_DYLD

#define USE_DYLD

#include <mach-o/dyld.h>

#else /* WITH_DYLD */

#define USE_RLD
/* Define this to 1 if you want be able to load ObjC modules as well:
   it switches between two different way of loading modules on the NeXT,
   one that directly interfaces with the dynamic loader (rld_load(), which
   does not correctly load ObjC object files), and another that uses the
   ObjC runtime (objc_loadModules()) to do the job.
   You'll have to add ``-ObjC'' to the compiler flags if you set this to 1.
*/
#define HANDLE_OBJC_MODULES 1
#if HANDLE_OBJC_MODULES
#include <objc/Object.h>
#include <objc/objc-load.h>
#endif

#include <mach-o/rld.h>

#endif /* WITH_DYLD */


const struct filedescr _PyImport_DynLoadFiletab[] = {
	{".so", "rb", C_EXTENSION},
	{"module.so", "rb", C_EXTENSION},
	{0, 0}
};

dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
				    const char *pathname, FILE *fp)
{
	dl_funcptr p = NULL;
	char funcname[258];

	PyOS_snprintf(funcname, sizeof(funcname), "_init%.200s", shortname);

#ifdef USE_RLD
	{
		NXStream *errorStream;
		struct mach_header *new_header;
		const char *filenames[2];
		long ret;
		unsigned long ptr;

		errorStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
		filenames[0] = pathname;
		filenames[1] = NULL;

#if HANDLE_OBJC_MODULES

/* The following very bogus line of code ensures that
   objc_msgSend, etc are linked into the binary.  Without
   it, dynamic loading of a module that includes objective-c
   method calls will fail with "undefined symbol _objc_msgSend()".
   This remains true even in the presence of the -ObjC flag
   to the compiler
*/

		[Object name];

/* objc_loadModules() dynamically loads the object files
   indicated by the paths in filenames.  If there are any
   errors generated during loading -- typically due to the
   inability to find particular symbols -- an error message
   will be written to errorStream.
   It returns 0 if the module is successfully loaded, 1
   otherwise.
*/

		ret = !objc_loadModules(filenames, errorStream,
					NULL, &new_header, NULL);

#else /* !HANDLE_OBJC_MODULES */

		ret = rld_load(errorStream, &new_header, 
				filenames, NULL);

#endif /* HANDLE_OBJC_MODULES */

		/* extract the error messages for the exception */
		if(!ret) {
			char *streamBuf;
			int len, maxLen;

			NXPutc(errorStream, (char)0);

			NXGetMemoryBuffer(errorStream,
				&streamBuf, &len, &maxLen);
			PyErr_SetString(PyExc_ImportError, streamBuf);
		}

		if(ret && rld_lookup(errorStream, funcname, &ptr))
			p = (dl_funcptr) ptr;

		NXCloseMemory(errorStream, NX_FREEBUFFER);

		if(!ret)
			return NULL;
	}
#endif /* USE_RLD */
#ifdef USE_DYLD
	/* This is also NeXT-specific. However, frameworks (the new style
	of shared library) and rld() can't be used in the same program;
	instead, you have to use dyld, which is mostly unimplemented. */
	{
		NSObjectFileImageReturnCode rc;
		NSObjectFileImage image;
		NSModule newModule;
		NSSymbol theSym;
		const char *errString;
	
		if (NSIsSymbolNameDefined(funcname)) {
			theSym = NSLookupAndBindSymbol(funcname);
			p = (dl_funcptr)NSAddressOfSymbol(theSym);
			return p;
		}
		rc = NSCreateObjectFileImageFromFile(pathname, &image);
		switch(rc) {
		    default:
		    case NSObjectFileImageFailure:
		    case NSObjectFileImageFormat:
		    /* for these a message is printed on stderr by dyld */
			errString = "Can't create object file image";
			break;
		    case NSObjectFileImageSuccess:
			errString = NULL;
			break;
		    case NSObjectFileImageInappropriateFile:
			errString = "Inappropriate file type for dynamic loading";
			break;
		    case NSObjectFileImageArch:
			errString = "Wrong CPU type in object file";
			break;
		    case NSObjectFileImageAccess:
			errString = "Can't read object file (no access)";
			break;
		}
		if (errString == NULL) {
			newModule = NSLinkModule(image, pathname,
				NSLINKMODULE_OPTION_BINDNOW|NSLINKMODULE_OPTION_RETURN_ON_ERROR);
			if (!newModule)
				errString = "Failure linking new module";
		}
		if (errString != NULL) {
			PyErr_SetString(PyExc_ImportError, errString);
			return NULL;
		}
		if (!NSIsSymbolNameDefined(funcname)) {
			/* UnlinkModule() isn't implemented in current versions, but calling it does no harm */
			NSUnLinkModule(newModule, FALSE);
			PyErr_Format(PyExc_ImportError,
				     "Loaded module does not contain symbol %.200s",
				     funcname);
			return NULL;
		}
		theSym = NSLookupAndBindSymbol(funcname);
		p = (dl_funcptr)NSAddressOfSymbol(theSym);
 	}
#endif /* USE_DYLD */

	return p;
}
