Fixed some issues found by winapi_check.
[wine] / library / port.c
1 /*
2  * Misc. functions for systems that don't have them
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #ifdef __BEOS__
25 #include <be/kernel/fs_info.h>
26 #include <be/kernel/OS.h>
27 #endif
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #include <sys/types.h>
39 #ifdef HAVE_SYS_INTTYPES_H
40 # include <sys/inttypes.h>
41 #endif
42 #ifdef HAVE_SYS_TIME_h
43 # include <sys/time.h>
44 #endif
45 #include <sys/stat.h>
46 #ifdef HAVE_SYS_IOCTL_H
47 #include <sys/ioctl.h>
48 #endif
49 #include <errno.h>
50 #include <fcntl.h>
51 #ifdef HAVE_TERMIOS_H
52 #include <termios.h>
53 #endif
54 #ifdef HAVE_SYS_MMAN_H
55 #include <sys/mman.h>
56 #endif
57 #ifdef HAVE_LIBIO_H
58 # include <libio.h>
59 #endif
60 #ifdef HAVE_SYSCALL_H
61 # include <syscall.h>
62 #endif
63 #ifdef HAVE_STDINT_H
64 # include <stdint.h>
65 #endif
66
67 /***********************************************************************
68  *              usleep
69  */
70 #ifndef HAVE_USLEEP
71 int usleep (unsigned int useconds)
72 {
73 #if defined(__EMX__)
74     DosSleep(useconds);
75     return 0;
76 #elif defined(__BEOS__)
77     return snooze(useconds);
78 #elif defined(HAVE_SELECT)
79     struct timeval delay;
80
81     delay.tv_sec = useconds / 1000000;
82     delay.tv_usec = useconds % 1000000;
83
84     select( 0, 0, 0, 0, &delay );
85     return 0;
86 #else /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
87     errno = ENOSYS;
88     return -1;
89 #endif /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
90 }
91 #endif /* HAVE_USLEEP */
92
93 /***********************************************************************
94  *              memmove
95  */
96 #ifndef HAVE_MEMMOVE
97 void *memmove( void *dest, const void *src, unsigned int len )
98 {
99     register char *dst = dest;
100
101     /* Use memcpy if not overlapping */
102     if ((dst + len <= (char *)src) || ((char *)src + len <= dst))
103     {
104         memcpy( dst, src, len );
105     }
106     /* Otherwise do it the hard way (FIXME: could do better than this) */
107     else if (dst < (char *)src)
108     {
109         while (len--) *dst++ = *((char *)src)++;
110     }
111     else
112     {
113         dst += len - 1;
114         src = (char *)src + len - 1;
115         while (len--) *dst-- = *((char *)src)--;
116     }
117     return dest;
118 }
119 #endif  /* HAVE_MEMMOVE */
120
121 /***********************************************************************
122  *              strerror
123  */
124 #ifndef HAVE_STRERROR
125 const char *strerror( int err )
126 {
127     /* Let's hope we have sys_errlist then */
128     return sys_errlist[err];
129 }
130 #endif  /* HAVE_STRERROR */
131
132
133 /***********************************************************************
134  *              getpagesize
135  */
136 #ifndef HAVE_GETPAGESIZE
137 size_t getpagesize(void)
138 {
139 # ifdef __svr4__
140     return sysconf(_SC_PAGESIZE);
141 # elif defined(__i386__)
142     return 4096;
143 # else
144 #  error Cannot get the page size on this platform
145 # endif
146 }
147 #endif  /* HAVE_GETPAGESIZE */
148
149
150 /***********************************************************************
151  *              clone
152  */
153 #if !defined(HAVE_CLONE) && defined(__linux__)
154 int clone( int (*fn)(void *), void *stack, int flags, void *arg )
155 {
156 #ifdef __i386__
157     int ret;
158     void **stack_ptr = (void **)stack;
159     *--stack_ptr = arg;  /* Push argument on stack */
160     *--stack_ptr = fn;   /* Push function pointer (popped into ebx) */
161     __asm__ __volatile__( "pushl %%ebx\n\t"
162                           "movl %2,%%ebx\n\t"
163                           "int $0x80\n\t"
164                           "popl %%ebx\n\t"   /* Contains fn in the child */
165                           "testl %%eax,%%eax\n\t"
166                           "jnz 0f\n\t"
167                           "xorl %ebp,%ebp\n\t"    /* Terminate the stack frames */
168                           "call *%%ebx\n\t"       /* Should never return */
169                           "xorl %%eax,%%eax\n\t"  /* Just in case it does*/
170                           "0:"
171                           : "=a" (ret)
172                           : "0" (SYS_clone), "r" (flags), "c" (stack_ptr) );
173     assert( ret );  /* If ret is 0, we returned from the child function */
174     if (ret > 0) return ret;
175     errno = -ret;
176     return -1;
177 #else
178     errno = EINVAL;
179     return -1;
180 #endif  /* __i386__ */
181 }
182 #endif  /* !HAVE_CLONE && __linux__ */
183
184 /***********************************************************************
185  *              strcasecmp
186  */
187 #ifndef HAVE_STRCASECMP
188 int strcasecmp( const char *str1, const char *str2 )
189 {
190     const unsigned char *ustr1 = (const unsigned char *)str1;
191     const unsigned char *ustr2 = (const unsigned char *)str2;
192
193     while (*ustr1 && toupper(*ustr1) == toupper(*ustr2)) {
194         ustr1++;
195         ustr2++;
196     }
197     return toupper(*ustr1) - toupper(*ustr2);
198 }
199 #endif /* HAVE_STRCASECMP */
200
201 /***********************************************************************
202  *              strncasecmp
203  */
204 #ifndef HAVE_STRNCASECMP
205 int strncasecmp( const char *str1, const char *str2, size_t n )
206 {
207     const unsigned char *ustr1 = (const unsigned char *)str1;
208     const unsigned char *ustr2 = (const unsigned char *)str2;
209     int res;
210
211     if (!n) return 0;
212     while ((--n > 0) && *ustr1) {
213         if ((res = toupper(*ustr1) - toupper(*ustr2))) return res;
214         ustr1++;
215         ustr2++;
216     }
217     return toupper(*ustr1) - toupper(*ustr2);
218 }
219 #endif /* HAVE_STRNCASECMP */
220
221 /***********************************************************************
222  *              getsockopt
223  */
224 #ifndef HAVE_GETSOCKOPT
225 int getsockopt(int socket, int level, int option_name,
226                void *option_value, size_t *option_len)
227 {
228     errno = ENOSYS;
229     return -1;
230 }
231 #endif /* !defined(HAVE_GETSOCKOPT) */
232
233 /***********************************************************************
234  *              inet_network
235  */
236 #ifndef HAVE_INET_NETWORK
237 unsigned long inet_network(const char *cp)
238 {
239     errno = ENOSYS;
240     return 0;
241 }
242 #endif /* defined(HAVE_INET_NETWORK) */
243
244 /***********************************************************************
245  *              statfs
246  */
247 #ifndef HAVE_STATFS
248 int statfs(const char *name, struct statfs *info)
249 {
250 #ifdef __BEOS__
251     dev_t mydev;
252     fs_info fsinfo;
253
254     if(!info) {
255         errno = ENOSYS;
256         return -1;
257     }
258
259     if ((mydev = dev_for_path(name)) < 0) {
260         errno = ENOSYS;
261         return -1;
262     }
263
264     if (fs_stat_dev(mydev,&fsinfo) < 0) {
265         errno = ENOSYS;
266         return -1;
267     }
268
269     info->f_bsize = fsinfo.block_size;
270     info->f_blocks = fsinfo.total_blocks;
271     info->f_bfree = fsinfo.free_blocks;
272     return 0;
273 #else /* defined(__BEOS__) */
274     errno = ENOSYS;
275     return -1;
276 #endif /* defined(__BEOS__) */
277 }
278 #endif /* !defined(HAVE_STATFS) */
279
280
281 /***********************************************************************
282  *              lstat
283  */
284 #ifndef HAVE_LSTAT
285 int lstat(const char *file_name, struct stat *buf)
286 {
287     return stat( file_name, buf );
288 }
289 #endif /* HAVE_LSTAT */
290
291 /***********************************************************************
292  *              mkstemp
293  */
294 #ifndef HAVE_MKSTEMP
295 int mkstemp(char *tmpfn)
296 {
297     int tries;
298     char *xstart;
299
300     xstart = tmpfn+strlen(tmpfn)-1;
301     while ((xstart > tmpfn) && (*xstart == 'X'))
302         xstart--;
303     tries = 10;
304     while (tries--) {
305         char *newfn = mktemp(tmpfn);
306         int fd;
307         if (!newfn) /* something else broke horribly */
308             return -1;
309         fd = open(newfn,O_CREAT|O_RDWR|O_EXCL,0600);
310         if (fd!=-1)
311             return fd;
312         newfn = xstart;
313         /* fill up with X and try again ... */
314         while (*newfn) *newfn++ = 'X';
315     }
316     return -1;
317 }
318 #endif /* HAVE_MKSTEMP */
319
320
321 /***********************************************************************
322  *              pread
323  *
324  * FIXME: this is not thread-safe
325  */
326 #ifndef HAVE_PREAD
327 ssize_t pread( int fd, void *buf, size_t count, off_t offset )
328 {
329     ssize_t ret;
330     off_t old_pos;
331
332     if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1;
333     if (lseek( fd, offset, SEEK_SET ) == -1) return -1;
334     if ((ret = read( fd, buf, count )) == -1)
335     {
336         int err = errno;  /* save errno */
337         lseek( fd, old_pos, SEEK_SET );
338         errno = err;
339         return -1;
340     }
341     if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
342     return ret;
343 }
344 #endif /* HAVE_PREAD */
345
346
347 /***********************************************************************
348  *              pwrite
349  *
350  * FIXME: this is not thread-safe
351  */
352 #ifndef HAVE_PWRITE
353 ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset )
354 {
355     ssize_t ret;
356     off_t old_pos;
357
358     if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1;
359     if (lseek( fd, offset, SEEK_SET ) == -1) return -1;
360     if ((ret = write( fd, buf, count )) == -1)
361     {
362         int err = errno;  /* save errno */
363         lseek( fd, old_pos, SEEK_SET );
364         errno = err;
365         return -1;
366     }
367     if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
368     return ret;
369 }
370 #endif /* HAVE_PWRITE */
371
372
373 #if defined(__svr4__) || defined(__NetBSD__)
374 /***********************************************************************
375  *             try_mmap_fixed
376  *
377  * The purpose of this routine is to emulate the behaviour of
378  * the Linux mmap() routine if a non-NULL address is passed,
379  * but the MAP_FIXED flag is not set.  Linux in this case tries
380  * to place the mapping at the specified address, *unless* the
381  * range is already in use.  Solaris, however, completely ignores
382  * the address argument in this case.
383  *
384  * As Wine code occasionally relies on the Linux behaviour, e.g. to
385  * be able to map non-relocateable PE executables to their proper
386  * start addresses, or to map the DOS memory to 0, this routine
387  * emulates the Linux behaviour by checking whether the desired
388  * address range is still available, and placing the mapping there
389  * using MAP_FIXED if so.
390  */
391 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
392                            int fildes, off_t off)
393 {
394     char * volatile result = NULL;
395     int pagesize = getpagesize();
396     pid_t pid;
397
398     /* We only try to map to a fixed address if
399        addr is non-NULL and properly aligned,
400        and MAP_FIXED isn't already specified. */
401
402     if ( !addr )
403         return 0;
404     if ( (uintptr_t)addr & (pagesize-1) )
405         return 0;
406     if ( flags & MAP_FIXED )
407         return 0;
408
409     /* We use vfork() to freeze all threads of the
410        current process.  This allows us to check without
411        race condition whether the desired memory range is
412        already in use.  Note that because vfork() shares
413        the address spaces between parent and child, we
414        can actually perform the mapping in the child. */
415
416     if ( (pid = vfork()) == -1 )
417     {
418         perror("try_mmap_fixed: vfork");
419         exit(1);
420     }
421     if ( pid == 0 )
422     {
423         int i;
424         char vec;
425
426         /* We call mincore() for every page in the desired range.
427            If any of these calls succeeds, the page is already
428            mapped and we must fail. */
429         for ( i = 0; i < len; i += pagesize )
430             if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 )
431                _exit(1);
432
433         /* Perform the mapping with MAP_FIXED set.  This is safe
434            now, as none of the pages is currently in use. */
435         result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
436         if ( result == addr )
437             _exit(0);
438
439         if ( result != (void *) -1 ) /* This should never happen ... */
440             munmap( result, len );
441
442        _exit(1);
443     }
444
445     /* vfork() lets the parent continue only after the child
446        has exited.  Furthermore, Wine sets SIGCHLD to SIG_IGN,
447        so we don't need to wait for the child. */
448
449     return result == addr;
450 }
451 #endif
452
453 /***********************************************************************
454  *              wine_anon_mmap
455  *
456  * Portable wrapper for anonymous mmaps
457  */
458 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
459 {
460 #ifdef HAVE_MMAP
461     static int fdzero = -1;
462
463 #ifdef MAP_ANON
464     flags |= MAP_ANON;
465 #else
466     if (fdzero == -1)
467     {
468         if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
469         {
470             perror( "/dev/zero: open" );
471             exit(1);
472         }
473     }
474 #endif  /* MAP_ANON */
475
476 #ifdef MAP_SHARED
477     flags &= ~MAP_SHARED;
478 #endif
479
480     /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
481 #ifdef MAP_PRIVATE
482     flags |= MAP_PRIVATE;
483 #endif
484
485 #if defined(__svr4__) || defined(__NetBSD__)
486     if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
487         return start;
488 #endif
489
490     return mmap( start, size, prot, flags, fdzero, 0 );
491 #else
492     return (void *)-1;
493 #endif
494 }
495
496
497 /*
498  * These functions provide wrappers around dlopen() and associated
499  * functions.  They work around a bug in glibc 2.1.x where calling
500  * a dl*() function after a previous dl*() function has failed
501  * without a dlerror() call between the two will cause a crash.
502  * They all take a pointer to a buffer that
503  * will receive the error description (from dlerror()).  This
504  * parameter may be NULL if the error description is not required.
505  */
506
507 /***********************************************************************
508  *              wine_dlopen
509  */
510 void *wine_dlopen( const char *filename, int flag, char *error, int errorsize )
511 {
512 #ifdef HAVE_DLOPEN
513     void *ret;
514     const char *s;
515     dlerror(); dlerror();
516     ret = dlopen( filename, flag );
517     s = dlerror();
518     if (error)
519     {
520         strncpy( error, s ? s : "", errorsize );
521         error[errorsize - 1] = '\0';
522     }
523     dlerror();
524     return ret;
525 #else
526     if (error)
527     {
528         strncpy( error, "dlopen interface not detected by configure", errorsize );
529         error[errorsize - 1] = '\0';
530     }
531     return NULL;
532 #endif
533 }
534
535 /***********************************************************************
536  *              wine_dlsym
537  */
538 void *wine_dlsym( void *handle, const char *symbol, char *error, int errorsize )
539 {
540 #ifdef HAVE_DLOPEN
541     void *ret;
542     const char *s;
543     dlerror(); dlerror();
544     ret = dlsym( handle, symbol );
545     s = dlerror();
546     if (error)
547     {
548         strncpy( error, s ? s : "", errorsize );
549         error[errorsize - 1] = '\0';
550     }
551     dlerror();
552     return ret;
553 #else
554     if (error)
555     {
556         strncpy( error, "dlopen interface not detected by configure", errorsize );
557         error[errorsize - 1] = '\0';
558     }
559     return NULL;
560 #endif
561 }
562
563 /***********************************************************************
564  *              wine_dlclose
565  */
566 int wine_dlclose( void *handle, char *error, int errorsize )
567 {
568 #ifdef HAVE_DLOPEN
569     int ret;
570     const char *s;
571     dlerror(); dlerror();
572     ret = dlclose( handle );
573     s = dlerror();
574     if (error)
575     {
576         strncpy( error, s ? s : "", errorsize );
577         error[errorsize - 1] = '\0';
578     }
579     dlerror();
580     return ret;
581 #else
582     if (error)
583     {
584         strncpy( error, "dlopen interface not detected by configure", errorsize );
585         error[errorsize - 1] = '\0';
586     }
587     return 1;
588 #endif
589 }
590
591 /***********************************************************************
592  *              wine_rewrite_s4tos2
593  *
594  * Convert 4 byte Unicode strings to 2 byte Unicode strings in-place.
595  * This is only practical if literal strings are writable.
596  */
597 unsigned short* wine_rewrite_s4tos2(const wchar_t* str4 )
598 {
599     unsigned short *str2,*s2;
600
601     if (str4==NULL)
602       return NULL;
603
604     if ((*str4 & 0xffff0000) != 0) {
605         /* This string has already been converted. Return it as is */
606         return (unsigned short*)str4;
607     }
608
609     /* Note that we can also end up here if the string has a single
610      * character. In such a case we will convert the string over and
611      * over again. But this is harmless.
612      */
613     str2=s2=(unsigned short*)str4;
614     do {
615         *s2=(unsigned short)*str4;
616         s2++;
617     } while (*str4++ != L'\0');
618
619     return str2;
620 }
621
622 #ifndef HAVE_ECVT
623 /*
624  * NetBSD 1.5 doesn't have ecvt, fcvt, gcvt. We just check for ecvt, though.
625  * Fix/verify these implementations !
626  */
627
628 /***********************************************************************
629  *              ecvt
630  */
631 char *ecvt (double number, int  ndigits,  int  *decpt,  int *sign)
632 {
633     static char buf[40]; /* ought to be enough */
634     char *dec;
635     sprintf(buf, "%.*e", ndigits /* FIXME wrong */, number);
636     *sign = (number < 0);
637     dec = strchr(buf, '.');
638     *decpt = (dec) ? (int)dec - (int)buf : -1;
639     return buf;
640 }
641
642 /***********************************************************************
643  *              fcvt
644  */
645 char *fcvt (double number, int  ndigits,  int  *decpt,  int *sign)
646 {
647     static char buf[40]; /* ought to be enough */
648     char *dec;
649     sprintf(buf, "%.*e", ndigits, number);
650     *sign = (number < 0);
651     dec = strchr(buf, '.');
652     *decpt = (dec) ? (int)dec - (int)buf : -1;
653     return buf;
654 }
655
656 /***********************************************************************
657  *              gcvt
658  *
659  * FIXME: uses both E and F.
660  */
661 char *gcvt (double number, size_t  ndigit,  char *buff)
662 {
663     sprintf(buff, "%.*E", (int)ndigit, number);
664     return buff;
665 }
666 #endif /* HAVE_ECVT */
667
668
669 /***********************************************************************
670  *              interlocked functions
671  */
672 #ifdef __i386__
673
674 #ifdef __GNUC__
675
676 __ASM_GLOBAL_FUNC(interlocked_cmpxchg,
677                   "movl 12(%esp),%eax\n\t"
678                   "movl 8(%esp),%ecx\n\t"
679                   "movl 4(%esp),%edx\n\t"
680                   "lock; cmpxchgl %ecx,(%edx)\n\t"
681                   "ret");
682 __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr,
683                   "movl 12(%esp),%eax\n\t"
684                   "movl 8(%esp),%ecx\n\t"
685                   "movl 4(%esp),%edx\n\t"
686                   "lock; cmpxchgl %ecx,(%edx)\n\t"
687                   "ret");
688 __ASM_GLOBAL_FUNC(interlocked_xchg,
689                   "movl 8(%esp),%eax\n\t"
690                   "movl 4(%esp),%edx\n\t"
691                   "lock; xchgl %eax,(%edx)\n\t"
692                   "ret");
693 __ASM_GLOBAL_FUNC(interlocked_xchg_ptr,
694                   "movl 8(%esp),%eax\n\t"
695                   "movl 4(%esp),%edx\n\t"
696                   "lock; xchgl %eax,(%edx)\n\t"
697                   "ret");
698 __ASM_GLOBAL_FUNC(interlocked_xchg_add,
699                   "movl 8(%esp),%eax\n\t"
700                   "movl 4(%esp),%edx\n\t"
701                   "lock; xaddl %eax,(%edx)\n\t"
702                   "ret");
703
704 #elif defined(_MSC_VER)
705
706 __declspec(naked) long interlocked_cmpxchg( long *dest, long xchg, long compare )
707 {
708     __asm mov eax, 12[esp];
709     __asm mov ecx, 8[esp];
710     __asm mov edx, 4[esp];
711     __asm lock cmpxchg [edx], ecx;
712     __asm ret;
713 }
714
715 __declspec(naked) void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
716 {
717     __asm mov eax, 12[esp];
718     __asm mov ecx, 8[esp];
719     __asm mov edx, 4[esp];
720     __asm lock cmpxchg [edx], ecx;
721     __asm ret;
722 }
723
724 __declspec(naked) long interlocked_xchg( long *dest, long val )
725 {
726     __asm mov eax, 8[esp];
727     __asm mov edx, 4[esp];
728     __asm lock xchg [edx], eax;
729     __asm ret;
730 }
731
732 __declspec(naked) void *interlocked_xchg_ptr( void **dest, void *val )
733 {
734     __asm mov eax, 8[esp];
735     __asm mov edx, 4[esp];
736     __asm lock xchg [edx], eax;
737     __asm ret;
738 }
739
740 __declspec(naked) long interlocked_xchg_add( long *dest, long incr )
741 {
742     __asm mov eax, 8[esp];
743     __asm mov edx, 4[esp];
744     __asm lock xadd [edx], eax;
745     __asm ret;
746 }
747
748 #else
749 # error You must implement the interlocked* functions for your compiler
750 #endif
751
752 #elif defined(__powerpc__)
753 void* interlocked_cmpxchg_ptr( void **dest, void* xchg, void* compare)
754 {
755     long ret = 0;
756     long scratch;
757     __asm__ __volatile__(
758     "0:    lwarx %0,0,%2 ;"
759     "      xor. %1,%4,%0;"
760     "      bne 1f;"
761     "      stwcx. %3,0,%2;"
762     "      bne- 0b;"
763     "1:    "
764     : "=&r"(ret), "=&r"(scratch)
765     : "r"(dest), "r"(xchg), "r"(compare)
766     : "cr0","memory");
767     return (void*)ret;
768 }
769
770 long interlocked_cmpxchg( long *dest, long xchg, long compare)
771 {
772     long ret = 0;
773     long scratch;
774     __asm__ __volatile__(
775     "0:    lwarx %0,0,%2 ;"
776     "      xor. %1,%4,%0;"
777     "      bne 1f;"
778     "      stwcx. %3,0,%2;"
779     "      bne- 0b;"
780     "1:    "
781     : "=&r"(ret), "=&r"(scratch)
782     : "r"(dest), "r"(xchg), "r"(compare)
783     : "cr0","memory");
784     return ret;
785 }
786
787 long interlocked_xchg_add( long *dest, long incr )
788 {
789     long ret = 0;
790     long zero = 0;
791     __asm__ __volatile__(
792         "0:    lwarx %0, %3, %1;"
793         "      add %0, %2, %0;"
794         "      stwcx. %0, %3, %1;"
795         "      bne- 0b;"
796         : "=&r" (ret)
797         : "r"(dest), "r"(incr), "r"(zero)
798         : "cr0", "memory"
799     );
800     return ret-incr;
801 }
802
803 long interlocked_xchg( long* dest, long val )
804 {
805     long ret = 0;
806     __asm__ __volatile__(
807     "0:    lwarx %0,0,%1 ;"
808     "      stwcx. %2,0,%1;"
809     "      bne- 0b;"
810     : "=&r"(ret)
811     : "r"(dest), "r"(val)
812     : "cr0","memory");
813     return ret;
814 }
815
816 void* interlocked_xchg_ptr( void** dest, void* val )
817 {
818     void *ret = NULL;
819     __asm__ __volatile__(
820     "0:    lwarx %0,0,%1 ;"
821     "      stwcx. %2,0,%1;"
822     "      bne- 0b;"
823     : "=&r"(ret)
824     : "r"(dest), "r"(val)
825     : "cr0","memory");
826     return ret;
827 }
828
829 #elif defined(__sparc__) && defined(__sun__)
830
831 /*
832  * As the earlier Sparc processors lack necessary atomic instructions,
833  * I'm simply falling back to the library-provided _lwp_mutex routines
834  * to ensure mutual exclusion in a way appropriate for the current
835  * architecture.
836  *
837  * FIXME:  If we have the compare-and-swap instruction (Sparc v9 and above)
838  *         we could use this to speed up the Interlocked operations ...
839  */
840 #include <synch.h>
841 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
842
843 long interlocked_cmpxchg( long *dest, long xchg, long compare )
844 {
845     _lwp_mutex_lock( &interlocked_mutex );
846     if (*dest == compare) *dest = xchg;
847     else compare = *dest;
848     _lwp_mutex_unlock( &interlocked_mutex );
849     return compare;
850 }
851
852 void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
853 {
854     _lwp_mutex_lock( &interlocked_mutex );
855     if (*dest == compare) *dest = xchg;
856     else compare = *dest;
857     _lwp_mutex_unlock( &interlocked_mutex );
858     return compare;
859 }
860
861 long interlocked_xchg( long *dest, long val )
862 {
863     long retv;
864     _lwp_mutex_lock( &interlocked_mutex );
865     retv = *dest;
866     *dest = val;
867     _lwp_mutex_unlock( &interlocked_mutex );
868     return retv;
869 }
870
871 void *interlocked_xchg_ptr( void **dest, void *val )
872 {
873     long retv;
874     _lwp_mutex_lock( &interlocked_mutex );
875     retv = *dest;
876     *dest = val;
877     _lwp_mutex_unlock( &interlocked_mutex );
878     return retv;
879 }
880
881 long interlocked_xchg_add( long *dest, long incr )
882 {
883     long retv;
884     _lwp_mutex_lock( &interlocked_mutex );
885     retv = *dest;
886     *dest += incr;
887     _lwp_mutex_unlock( &interlocked_mutex );
888     return retv;
889 }
890 #else
891 # error You must implement the interlocked* functions for your CPU
892 #endif