Yet another tweak on AIX dynaloading.
[p5sagit/p5-mst-13.2.git] / ext / DynaLoader / dl_aix.xs
1 /* dl_aix.xs
2  *
3  * Written: 8/31/94 by Wayne Scott (wscott@ichips.intel.com)
4  *
5  *  All I did was take Jens-Uwe Mager's libdl emulation library for
6  *  AIX and merged it with the dl_dlopen.xs file to create a dynamic library
7  *  package that works for AIX.
8  *
9  *  I did change all malloc's, free's, strdup's, calloc's to use the perl
10  *  equilvant.  I also removed some stuff we will not need.  Call fini()
11  *  on statup...   It can probably be trimmed more.
12  */
13
14 #define PERLIO_NOT_STDIO 0
15
16 /*
17  * @(#)dlfcn.c  1.5 revision of 93/02/14  20:14:17
18  * This is an unpublished work copyright (c) 1992 Helios Software GmbH
19  * 3000 Hannover 1, Germany
20  */
21 #include "EXTERN.h"
22 #include "perl.h"
23 #include "XSUB.h"
24
25 /* When building as a 64-bit binary on AIX, define this to get the
26  * correct structure definitions.  Also determines the field-name
27  * macros and gates some logic in readEntries().  -- Steven N. Hirsch
28  * <hirschs@btv.ibm.com> */
29 #ifdef USE_64_BIT_ALL
30 #   define __XCOFF64__
31 #   define __XCOFF32__
32 #endif
33
34 #include <stdio.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <sys/types.h>
39 #include <sys/ldr.h>
40 #include <a.out.h>
41 #undef FREAD
42 #undef FWRITE
43 #include <ldfcn.h>
44
45 #ifdef USE_64_BIT_ALL
46 #   define AIX_SCNHDR SCNHDR_64
47 #   define AIX_LDHDR LDHDR_64
48 #   define AIX_LDSYM LDSYM_64
49 #   define AIX_LDHDRSZ LDHDRSZ_64
50 #else
51 #   define AIX_SCNHDR SCNHDR
52 #   define AIX_LDHDR LDHDR
53 #   define AIX_LDSYM LDSYM
54 #   define AIX_LDHDRSZ LDHDRSZ
55 #endif
56
57 /* When using Perl extensions written in C++ the longer versions
58  * of load() and unload() from libC and libC_r need to be used,
59  * otherwise statics in the extensions won't get initialized right.
60  * -- Stephanie Beals <bealzy@us.ibm.com> */
61
62 /* Older AIX C compilers cannot deal with C++ double-slash comments in
63    the ibmcxx and/or xlC includes.  Since we only need a single file,
64    be more fine-grained about what's included <hirschs@btv.ibm.com> */
65
66 #ifdef USE_libC /* The define comes, when it comes, from hints/aix.pl. */
67 #   define LOAD   loadAndInit
68 #   define UNLOAD terminateAndUnload
69 #   if defined(USE_vacpp_load_h)
70 #       include "/usr/vacpp/include/load.h"
71 #   elif defined(USE_ibmcxx_load_h)
72 #       include "/usr/ibmcxx/include/load.h"
73 #   elif defined(USE_xlC_load_h)
74 #       include "/usr/lpp/xlC/include/load.h"
75 #   elif defined(USE_load_h)
76 #       include "/usr/include/load.h"
77 #   endif
78 #else
79 #   define LOAD   load
80 #   define UNLOAD unload
81 #endif
82
83 /*
84  * AIX 4.3 does remove some useful definitions from ldfcn.h. Define
85  * these here to compensate for that lossage.
86  */
87 #ifndef BEGINNING
88 # define BEGINNING SEEK_SET
89 #endif
90 #ifndef FSEEK
91 # define FSEEK(ldptr,o,p)       fseek(IOPTR(ldptr),(p==BEGINNING)?(OFFSET(ldptr) +o):o,p)
92 #endif
93 #ifndef FREAD
94 # define FREAD(p,s,n,ldptr)     fread(p,s,n,IOPTR(ldptr))
95 #endif
96
97 /*
98  * We simulate dlopen() et al. through a call to load. Because AIX has
99  * no call to find an exported symbol we read the loader section of the
100  * loaded module and build a list of exported symbols and their virtual
101  * address.
102  */
103
104 typedef struct {
105         char            *name;          /* the symbols's name */
106         void            *addr;          /* its relocated virtual address */
107 } Export, *ExportPtr;
108
109 /*
110  * The void * handle returned from dlopen is actually a ModulePtr.
111  */
112 typedef struct Module {
113         struct Module   *next;
114         char            *name;          /* module name for refcounting */
115         int             refCnt;         /* the number of references */
116         void            *entry;         /* entry point from load */
117         int             nExports;       /* the number of exports found */
118         ExportPtr       exports;        /* the array of exports */
119 } Module, *ModulePtr;
120
121 /*
122  * We keep a list of all loaded modules to be able to reference count
123  * duplicate dlopen's.
124  */
125 static ModulePtr modList;               /* XXX threaded */
126
127 /*
128  * The last error from one of the dl* routines is kept in static
129  * variables here. Each error is returned only once to the caller.
130  */
131 static char errbuf[BUFSIZ];             /* XXX threaded */
132 static int errvalid;                    /* XXX threaded */
133
134 static void caterr(char *);
135 static int readExports(ModulePtr);
136 static void *findMain(void);
137
138 static char *strerror_failed   = "(strerror failed)";
139 static char *strerror_r_failed = "(strerror_r failed)";
140
141 char *strerrorcat(char *str, int err) {
142     int strsiz = strlen(str);
143     int msgsiz;
144     char *msg;
145
146 #ifdef USE_THREADS
147     char *buf = malloc(BUFSIZ);
148
149     if (buf == 0)
150       return 0;
151     if (strerror_r(err, buf, BUFSIZ) == 0)
152       msg = buf;
153     else
154       msg = strerror_r_failed;
155     msgsiz = strlen(msg);
156     if (strsiz + msgsiz < BUFSIZ)
157       strcat(str, msg);
158     free(buf);
159 #else
160     if ((msg = strerror(err)) == 0)
161       msg = strerror_failed;
162     msgsiz = strlen(msg);               /* Note msg = buf and free() above. */
163     if (strsiz + msgsiz < BUFSIZ)       /* Do not move this after #endif. */
164       strcat(str, msg);
165 #endif
166
167     return str;
168 }
169
170 char *strerrorcpy(char *str, int err) {
171     int msgsiz;
172     char *msg;
173
174 #ifdef USE_THREADS
175     char *buf = malloc(BUFSIZ);
176
177     if (buf == 0)
178       return 0;
179     if (strerror_r(err, buf, BUFSIZ) == 0)
180       msg = buf;
181     else
182       msg = strerror_r_failed;
183     msgsiz = strlen(msg);
184     if (msgsiz < BUFSIZ)
185       strcpy(str, msg);
186     free(buf);
187 #else
188     if ((msg = strerror(err)) == 0)
189       msg = strerror_failed;
190     msgsiz = strlen(msg);       /* Note msg = buf and free() above. */
191     if (msgsiz < BUFSIZ)        /* Do not move this after #endif. */
192       strcpy(str, msg);
193 #endif
194
195     return str;
196 }
197   
198 /* ARGSUSED */
199 void *dlopen(char *path, int mode)
200 {
201         dTHX;
202         register ModulePtr mp;
203         static void *mainModule;                /* XXX threaded */
204
205         /*
206          * Upon the first call register a terminate handler that will
207          * close all libraries.
208          */
209         if (mainModule == NULL) {
210                 if ((mainModule = findMain()) == NULL)
211                         return NULL;
212         }
213         /*
214          * Scan the list of modules if have the module already loaded.
215          */
216         for (mp = modList; mp; mp = mp->next)
217                 if (strcmp(mp->name, path) == 0) {
218                         mp->refCnt++;
219                         return mp;
220                 }
221         Newz(1000,mp,1,Module);
222         if (mp == NULL) {
223                 errvalid++;
224                 strcpy(errbuf, "Newz: ");
225                 strerrorcat(errbuf, errno);
226                 return NULL;
227         }
228         
229         if ((mp->name = savepv(path)) == NULL) {
230                 errvalid++;
231                 strcpy(errbuf, "savepv: ");
232                 strerrorcat(errbuf, errno);
233                 safefree(mp);
234                 return NULL;
235         }
236
237         /*
238          * load should be declared load(const char *...). Thus we
239          * cast the path to a normal char *. Ugly.
240          */
241         if ((mp->entry = (void *)LOAD((char *)path,
242 #ifdef L_LIBPATH_EXEC
243                                       L_LIBPATH_EXEC |
244 #endif
245                                       L_NOAUTODEFER,
246                                       NULL)) == NULL) {
247                 int saverrno = errno;
248                 
249                 safefree(mp->name);
250                 safefree(mp);
251                 errvalid++;
252                 strcpy(errbuf, "dlopen: ");
253                 strcat(errbuf, path);
254                 strcat(errbuf, ": ");
255                 /*
256                  * If AIX says the file is not executable, the error
257                  * can be further described by querying the loader about
258                  * the last error.
259                  */
260                 if (saverrno == ENOEXEC) {
261                         char *moreinfo[BUFSIZ/sizeof(char *)];
262                         if (loadquery(L_GETMESSAGES, moreinfo, sizeof(moreinfo)) == -1)
263                                 strerrorcpy(errbuf, saverrno);
264                         else {
265                                 char **p;
266                                 for (p = moreinfo; *p; p++)
267                                         caterr(*p);
268                         }
269                 } else
270                         strerrorcat(errbuf, saverrno);
271                 return NULL;
272         }
273         mp->refCnt = 1;
274         mp->next = modList;
275         modList = mp;
276         /*
277          * Assume anonymous exports come from the module this dlopen
278          * is linked into, that holds true as long as dlopen and all
279          * of the perl core are in the same shared object. Also bind
280          * against the main part, in the case a perl is not the main
281          * part, e.g mod_perl as DSO in Apache so perl modules can
282          * also reference Apache symbols.
283          */
284         if (loadbind(0, (void *)dlopen, mp->entry) == -1 ||
285             loadbind(0, mainModule, mp->entry)) {
286                 int saverrno = errno;
287
288                 dlclose(mp);
289                 errvalid++;
290                 strcpy(errbuf, "loadbind: ");
291                 strerrorcat(errbuf, saverrno);
292                 return NULL;
293         }
294         if (readExports(mp) == -1) {
295                 dlclose(mp);
296                 return NULL;
297         }
298         return mp;
299 }
300
301 /*
302  * Attempt to decipher an AIX loader error message and append it
303  * to our static error message buffer.
304  */
305 static void caterr(char *s)
306 {
307         register char *p = s;
308
309         while (*p >= '0' && *p <= '9')
310                 p++;
311         switch(atoi(s)) {
312         case L_ERROR_TOOMANY:
313                 strcat(errbuf, "too many errors");
314                 break;
315         case L_ERROR_NOLIB:
316                 strcat(errbuf, "can't load library");
317                 strcat(errbuf, p);
318                 break;
319         case L_ERROR_UNDEF:
320                 strcat(errbuf, "can't find symbol");
321                 strcat(errbuf, p);
322                 break;
323         case L_ERROR_RLDBAD:
324                 strcat(errbuf, "bad RLD");
325                 strcat(errbuf, p);
326                 break;
327         case L_ERROR_FORMAT:
328                 strcat(errbuf, "bad exec format in");
329                 strcat(errbuf, p);
330                 break;
331         case L_ERROR_ERRNO:
332                 strerrorcat(errbuf, atoi(++p));
333                 break;
334         default:
335                 strcat(errbuf, s);
336                 break;
337         }
338 }
339
340 void *dlsym(void *handle, const char *symbol)
341 {
342         register ModulePtr mp = (ModulePtr)handle;
343         register ExportPtr ep;
344         register int i;
345
346         /*
347          * Could speed up search, but I assume that one assigns
348          * the result to function pointers anyways.
349          */
350         for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
351                 if (strcmp(ep->name, symbol) == 0)
352                         return ep->addr;
353         errvalid++;
354         strcpy(errbuf, "dlsym: undefined symbol ");
355         strcat(errbuf, symbol);
356         return NULL;
357 }
358
359 char *dlerror(void)
360 {
361         if (errvalid) {
362                 errvalid = 0;
363                 return errbuf;
364         }
365         return NULL;
366 }
367
368 int dlclose(void *handle)
369 {
370         register ModulePtr mp = (ModulePtr)handle;
371         int result;
372         register ModulePtr mp1;
373
374         if (--mp->refCnt > 0)
375                 return 0;
376         result = UNLOAD(mp->entry);
377         if (result == -1) {
378                 errvalid++;
379                 strerrorcpy(errbuf, errno);
380         }
381         if (mp->exports) {
382                 register ExportPtr ep;
383                 register int i;
384                 for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
385                         if (ep->name)
386                                 safefree(ep->name);
387                 safefree(mp->exports);
388         }
389         if (mp == modList)
390                 modList = mp->next;
391         else {
392                 for (mp1 = modList; mp1; mp1 = mp1->next)
393                         if (mp1->next == mp) {
394                                 mp1->next = mp->next;
395                                 break;
396                         }
397         }
398         safefree(mp->name);
399         safefree(mp);
400         return result;
401 }
402
403 /* Added by Wayne Scott 
404  * This is needed because the ldopen system call calls
405  * calloc to allocated a block of date.  The ldclose call calls free.
406  * Without this we get this system calloc and perl's free, resulting
407  * in a "Bad free" message.  This way we always use perl's malloc.
408  */
409 void *calloc(size_t ne, size_t sz) 
410 {
411   void *out;
412
413   out = (void *) safemalloc(ne*sz);
414   memzero(out, ne*sz);
415   return(out);
416 }
417  
418 /*
419  * Build the export table from the XCOFF .loader section.
420  */
421 static int readExports(ModulePtr mp)
422 {
423         dTHX;
424         LDFILE *ldp = NULL;
425         AIX_SCNHDR sh;
426         AIX_LDHDR *lhp;
427         char *ldbuf;
428         AIX_LDSYM *ls;
429         int i;
430         ExportPtr ep;
431
432         if ((ldp = ldopen(mp->name, ldp)) == NULL) {
433                 struct ld_info *lp;
434                 char *buf;
435                 int size = 4*1024;
436                 if (errno != ENOENT) {
437                         errvalid++;
438                         strcpy(errbuf, "readExports: ");
439                         strerrorcat(errbuf, errno);
440                         return -1;
441                 }
442                 /*
443                  * The module might be loaded due to the LIBPATH
444                  * environment variable. Search for the loaded
445                  * module using L_GETINFO.
446                  */
447                 if ((buf = safemalloc(size)) == NULL) {
448                         errvalid++;
449                         strcpy(errbuf, "readExports: ");
450                         strerrorcat(errbuf, errno);
451                         return -1;
452                 }
453                 while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
454                         safefree(buf);
455                         size += 4*1024;
456                         if ((buf = safemalloc(size)) == NULL) {
457                                 errvalid++;
458                                 strcpy(errbuf, "readExports: ");
459                                 strerrorcat(errbuf, errno);
460                                 return -1;
461                         }
462                 }
463                 if (i == -1) {
464                         errvalid++;
465                         strcpy(errbuf, "readExports: ");
466                         strerrorcat(errbuf, errno);
467                         safefree(buf);
468                         return -1;
469                 }
470                 /*
471                  * Traverse the list of loaded modules. The entry point
472                  * returned by LOAD() does actually point to the data
473                  * segment origin.
474                  */
475                 lp = (struct ld_info *)buf;
476                 while (lp) {
477                         if (lp->ldinfo_dataorg == mp->entry) {
478                                 ldp = ldopen(lp->ldinfo_filename, ldp);
479                                 break;
480                         }
481                         if (lp->ldinfo_next == 0)
482                                 lp = NULL;
483                         else
484                                 lp = (struct ld_info *)((char *)lp + lp->ldinfo_next);
485                 }
486                 safefree(buf);
487                 if (!ldp) {
488                         errvalid++;
489                         strcpy(errbuf, "readExports: ");
490                         strerrorcat(errbuf, errno);
491                         return -1;
492                 }
493         }
494 #ifdef USE_64_BIT_ALL
495         if (TYPE(ldp) != U803XTOCMAGIC) {
496 #else
497         if (TYPE(ldp) != U802TOCMAGIC) {
498 #endif
499                 errvalid++;
500                 strcpy(errbuf, "readExports: bad magic");
501                 while(ldclose(ldp) == FAILURE)
502                         ;
503                 return -1;
504         }
505         if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
506                 errvalid++;
507                 strcpy(errbuf, "readExports: cannot read loader section header");
508                 while(ldclose(ldp) == FAILURE)
509                         ;
510                 return -1;
511         }
512         /*
513          * We read the complete loader section in one chunk, this makes
514          * finding long symbol names residing in the string table easier.
515          */
516         if ((ldbuf = (char *)safemalloc(sh.s_size)) == NULL) {
517                 errvalid++;
518                 strcpy(errbuf, "readExports: ");
519                 strerrorcat(errbuf, errno);
520                 while(ldclose(ldp) == FAILURE)
521                         ;
522                 return -1;
523         }
524         if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
525                 errvalid++;
526                 strcpy(errbuf, "readExports: cannot seek to loader section");
527                 safefree(ldbuf);
528                 while(ldclose(ldp) == FAILURE)
529                         ;
530                 return -1;
531         }
532 /* This first case is a hack, since it assumes that the 3rd parameter to
533    FREAD is 1. See the redefinition of FREAD above to see how this works. */
534         if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
535                 errvalid++;
536                 strcpy(errbuf, "readExports: cannot read loader section");
537                 safefree(ldbuf);
538                 while(ldclose(ldp) == FAILURE)
539                         ;
540                 return -1;
541         }
542         lhp = (AIX_LDHDR *)ldbuf;
543         ls = (AIX_LDSYM *)(ldbuf+AIX_LDHDRSZ);
544         /*
545          * Count the number of exports to include in our export table.
546          */
547         for (i = lhp->l_nsyms; i; i--, ls++) {
548                 if (!LDR_EXPORT(*ls))
549                         continue;
550                 mp->nExports++;
551         }
552         Newz(1001, mp->exports, mp->nExports, Export);
553         if (mp->exports == NULL) {
554                 errvalid++;
555                 strcpy(errbuf, "readExports: ");
556                 strerrorcat(errbuf, errno);
557                 safefree(ldbuf);
558                 while(ldclose(ldp) == FAILURE)
559                         ;
560                 return -1;
561         }
562         /*
563          * Fill in the export table. All entries are relative to
564          * the entry point we got from load.
565          */
566         ep = mp->exports;
567         ls = (AIX_LDSYM *)(ldbuf+AIX_LDHDRSZ);
568         for (i = lhp->l_nsyms; i; i--, ls++) {
569                 char *symname;
570                 if (!LDR_EXPORT(*ls))
571                         continue;
572 #ifndef USE_64_BIT_ALL
573                 if (ls->l_zeroes == 0)
574 #endif
575                         symname = ls->l_offset+lhp->l_stoff+ldbuf;
576 #ifndef USE_64_BIT_ALL
577                 else
578                         symname = ls->l_name;
579 #endif
580                 ep->name = savepv(symname);
581                 ep->addr = (void *)((unsigned long)mp->entry + ls->l_value);
582                 ep++;
583         }
584         safefree(ldbuf);
585         while(ldclose(ldp) == FAILURE)
586                 ;
587         return 0;
588 }
589
590 /*
591  * Find the main modules entry point. This is used as export pointer
592  * for loadbind() to be able to resolve references to the main part.
593  */
594 static void * findMain(void)
595 {
596         struct ld_info *lp;
597         char *buf;
598         int size = 4*1024;
599         int i;
600         void *ret;
601
602         if ((buf = safemalloc(size)) == NULL) {
603                 errvalid++;
604                 strcpy(errbuf, "findMain: ");
605                 strerrorcat(errbuf, errno);
606                 return NULL;
607         }
608         while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
609                 safefree(buf);
610                 size += 4*1024;
611                 if ((buf = safemalloc(size)) == NULL) {
612                         errvalid++;
613                         strcpy(errbuf, "findMain: ");
614                         strerrorcat(errbuf, errno);
615                         return NULL;
616                 }
617         }
618         if (i == -1) {
619                 errvalid++;
620                 strcpy(errbuf, "findMain: ");
621                 strerrorcat(errbuf, errno);
622                 safefree(buf);
623                 return NULL;
624         }
625         /*
626          * The first entry is the main module. The entry point
627          * returned by load() does actually point to the data
628          * segment origin.
629          */
630         lp = (struct ld_info *)buf;
631         ret = lp->ldinfo_dataorg;
632         safefree(buf);
633         return ret;
634 }
635
636 /* dl_dlopen.xs
637  * 
638  * Platform:    SunOS/Solaris, possibly others which use dlopen.
639  * Author:      Paul Marquess (Paul.Marquess@btinternet.com)
640  * Created:     10th July 1994
641  *
642  * Modified:
643  * 15th July 1994   - Added code to explicitly save any error messages.
644  * 3rd August 1994  - Upgraded to v3 spec.
645  * 9th August 1994  - Changed to use IV
646  * 10th August 1994 - Tim Bunce: Added RTLD_LAZY, switchable debugging,
647  *                    basic FreeBSD support, removed ClearError
648  *
649  */
650
651 /* Porting notes:
652
653         see dl_dlopen.xs
654
655 */
656
657 #include "dlutils.c"    /* SaveError() etc      */
658
659
660 static void
661 dl_private_init(pTHX)
662 {
663     (void)dl_generic_private_init(aTHX);
664 }
665  
666 MODULE = DynaLoader     PACKAGE = DynaLoader
667
668 BOOT:
669     (void)dl_private_init(aTHX);
670
671
672 void *
673 dl_load_file(filename, flags=0)
674         char *  filename
675         int     flags
676         CODE:
677         DLDEBUG(1,PerlIO_printf(Perl_debug_log, "dl_load_file(%s,%x):\n", filename,flags));
678         if (flags & 0x01)
679             Perl_warn(aTHX_ "Can't make loaded symbols global on this platform while loading %s",filename);
680         RETVAL = dlopen(filename, 1) ;
681         DLDEBUG(2,PerlIO_printf(Perl_debug_log, " libref=%x\n", RETVAL));
682         ST(0) = sv_newmortal() ;
683         if (RETVAL == NULL)
684             SaveError(aTHX_ "%s",dlerror()) ;
685         else
686             sv_setiv( ST(0), PTR2IV(RETVAL) );
687
688 int
689 dl_unload_file(libref)
690     void *      libref
691   CODE:
692     DLDEBUG(1,PerlIO_printf(Perl_debug_log, "dl_unload_file(%lx):\n", libref));
693     RETVAL = (dlclose(libref) == 0 ? 1 : 0);
694     if (!RETVAL)
695         SaveError(aTHX_ "%s", dlerror()) ;
696     DLDEBUG(2,PerlIO_printf(Perl_debug_log, " retval = %d\n", RETVAL));
697   OUTPUT:
698     RETVAL
699
700 void *
701 dl_find_symbol(libhandle, symbolname)
702         void *          libhandle
703         char *          symbolname
704         CODE:
705         DLDEBUG(2,PerlIO_printf(Perl_debug_log, "dl_find_symbol(handle=%x, symbol=%s)\n",
706                 libhandle, symbolname));
707         RETVAL = dlsym(libhandle, symbolname);
708         DLDEBUG(2,PerlIO_printf(Perl_debug_log, "  symbolref = %x\n", RETVAL));
709         ST(0) = sv_newmortal() ;
710         if (RETVAL == NULL)
711             SaveError(aTHX_ "%s",dlerror()) ;
712         else
713             sv_setiv( ST(0), PTR2IV(RETVAL));
714
715
716 void
717 dl_undef_symbols()
718         PPCODE:
719
720
721
722 # These functions should not need changing on any platform:
723
724 void
725 dl_install_xsub(perl_name, symref, filename="$Package")
726     char *      perl_name
727     void *      symref 
728     char *      filename
729     CODE:
730     DLDEBUG(2,PerlIO_printf(Perl_debug_log, "dl_install_xsub(name=%s, symref=%x)\n",
731         perl_name, symref));
732     ST(0) = sv_2mortal(newRV((SV*)newXS(perl_name,
733                                         (void(*)(pTHX_ CV *))symref,
734                                         filename)));
735
736
737 char *
738 dl_error()
739     CODE:
740     RETVAL = LastError ;
741     OUTPUT:
742     RETVAL
743
744 # end.