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