695 lines
18 KiB
C
695 lines
18 KiB
C
/*********************************************************
|
|
* Copyright (C) 1998-2015 VMware, Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation version 2.1 and no later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
|
|
* License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*********************************************************/
|
|
|
|
/*
|
|
* util.h --
|
|
*
|
|
* misc util functions
|
|
*/
|
|
|
|
#ifndef UTIL_H
|
|
#define UTIL_H
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#ifndef VMKBOOT
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#ifdef USERLEVEL
|
|
#include <tchar.h> /* Needed for MBCS string functions */
|
|
#include <windows.h> /* for definition of HANDLE */
|
|
#endif
|
|
#else
|
|
#include <sys/types.h>
|
|
#include "errno.h"
|
|
#endif
|
|
#include "vm_assert.h"
|
|
#include "vm_basic_defs.h"
|
|
#include "unicodeTypes.h"
|
|
|
|
|
|
/*
|
|
* Define the Util_ThreadID type, and assorted standard bits.
|
|
*/
|
|
#if defined(_WIN32)
|
|
typedef DWORD Util_ThreadID;
|
|
#else
|
|
#include <unistd.h>
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
#include <pthread.h>
|
|
typedef pthread_t Util_ThreadID;
|
|
#else // Linux et al
|
|
typedef pid_t Util_ThreadID;
|
|
#endif
|
|
#endif
|
|
|
|
uint32 CRC_Compute(const uint8 *buf, int len);
|
|
uint32 Util_Checksum32(const uint32 *buf, int len);
|
|
uint32 Util_Checksum(const uint8 *buf, int len);
|
|
uint32 Util_Checksumv(void *iov, int numEntries);
|
|
uint32 Util_HashString(const char *str);
|
|
Unicode Util_ExpandString(ConstUnicode fileName);
|
|
void Util_ExitThread(int);
|
|
NORETURN void Util_ExitProcessAbruptly(int);
|
|
int Util_HasAdminPriv(void);
|
|
#if defined _WIN32 && defined USERLEVEL
|
|
int Util_TokenHasAdminPriv(HANDLE token);
|
|
int Util_TokenHasInteractPriv(HANDLE token);
|
|
#endif
|
|
Bool Util_Data2Buffer(char *buf, size_t bufSize, const void *data0,
|
|
size_t dataSize);
|
|
char *Util_GetCanonicalPath(const char *path);
|
|
#ifdef _WIN32
|
|
char *Util_CompatGetCanonicalPath(const char *path);
|
|
char *Util_GetCanonicalPathForHash(const char *path);
|
|
char *Util_CompatGetLowerCaseCanonicalPath(const char* path);
|
|
#endif
|
|
int Util_BumpNoFds(uint32 *cur, uint32 *wanted);
|
|
Bool Util_CanonicalPathsIdentical(const char *path1, const char *path2);
|
|
Bool Util_IsAbsolutePath(const char *path);
|
|
unsigned Util_GetPrime(unsigned n0);
|
|
Util_ThreadID Util_GetCurrentThreadId(void);
|
|
|
|
char *Util_DeriveFileName(const char *source,
|
|
const char *name,
|
|
const char *ext);
|
|
|
|
char *Util_CombineStrings(char **sources, int count);
|
|
char **Util_SeparateStrings(char *source, int *count);
|
|
|
|
typedef struct UtilSingleUseResource UtilSingleUseResource;
|
|
UtilSingleUseResource *Util_SingleUseAcquire(const char *name);
|
|
void Util_SingleUseRelease(UtilSingleUseResource *res);
|
|
|
|
#ifndef _WIN32
|
|
Bool Util_IPv4AddrValid(const char *addr);
|
|
Bool Util_IPv6AddrValid(const char *addr);
|
|
Bool Util_IPAddrValid(const char *addr);
|
|
#endif
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(sun)
|
|
Bool Util_GetProcessName(pid_t pid, char *bufOut, size_t bufOutSize);
|
|
#endif
|
|
|
|
#if defined __linux__ && !defined VMX86_SERVER
|
|
Bool Util_IsPhysicalSSD(const char* device);
|
|
#endif
|
|
|
|
// backtrace functions and utilities
|
|
|
|
#define UTIL_BACKTRACE_LINE_LEN (511)
|
|
typedef void (*Util_OutputFunc)(void *data, const char *fmt, ...);
|
|
|
|
void Util_Backtrace(int bugNr);
|
|
void Util_BacktraceFromPointer(uintptr_t *basePtr);
|
|
void Util_BacktraceFromPointerWithFunc(uintptr_t *basePtr,
|
|
Util_OutputFunc outFunc,
|
|
void *outFuncData);
|
|
void Util_BacktraceWithFunc(int bugNr,
|
|
Util_OutputFunc outFunc,
|
|
void *outFuncData);
|
|
|
|
void Util_BacktraceToBuffer(uintptr_t *basePtr,
|
|
uintptr_t *buffer, int len);
|
|
|
|
// sleep functions
|
|
|
|
void Util_Usleep(long usec);
|
|
void Util_Sleep(unsigned int sec);
|
|
|
|
int Util_CompareDotted(const char *s1, const char *s2);
|
|
|
|
/*
|
|
* This enum defines how Util_GetOpt should handle non-option arguments:
|
|
*
|
|
* UTIL_NONOPT_PERMUTE: Permute argv so that all non-options are at the end.
|
|
* UTIL_NONOPT_STOP: Stop when first non-option argument is seen.
|
|
* UTIL_NONOPT_ALL: Return each non-option argument as if it were
|
|
* an option with character code 1.
|
|
*/
|
|
typedef enum { UTIL_NONOPT_PERMUTE, UTIL_NONOPT_STOP, UTIL_NONOPT_ALL } Util_NonOptMode;
|
|
struct option;
|
|
int Util_GetOpt(int argc, char * const *argv, const struct option *opts,
|
|
Util_NonOptMode mode);
|
|
|
|
|
|
#if defined(VMX86_STATS)
|
|
Bool Util_QueryCStResidency(uint32 *numCpus, uint32 *numCStates,
|
|
uint64 **transitns, uint64 **residency,
|
|
uint64 **transTime, uint64 **residTime);
|
|
#endif
|
|
|
|
/*
|
|
* In util_shared.h
|
|
*/
|
|
#define UTIL_FASTRAND_SEED_MAX (0x7fffffff)
|
|
Bool Util_Throttle(uint32 count);
|
|
uint32 Util_FastRand(uint32 seed);
|
|
|
|
|
|
// Not thread safe!
|
|
void Util_OverrideHomeDir(const char *path);
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_ValidateBytes --
|
|
*
|
|
* Check that memory is filled with the specified value.
|
|
*
|
|
* Results:
|
|
* NULL No error
|
|
* !NULL First address that doesn't have the proper value
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void *
|
|
Util_ValidateBytes(const void *ptr, // IN: ptr to check
|
|
size_t size, // IN: size of ptr
|
|
uint8 byteValue) // IN: memory must be filled with this
|
|
{
|
|
uint8 *p;
|
|
uint8 *end;
|
|
uint64 bigValue;
|
|
|
|
ASSERT(ptr);
|
|
|
|
if (size == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
p = (uint8 *) ptr;
|
|
end = p + size;
|
|
|
|
/* Compare bytes until a "nice" boundary is achieved. */
|
|
while ((uintptr_t) p % sizeof bigValue) {
|
|
if (*p != byteValue) {
|
|
return p;
|
|
}
|
|
|
|
p++;
|
|
|
|
if (p == end) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Compare using a "nice sized" chunk for a long as possible. */
|
|
memset(&bigValue, (int) byteValue, sizeof bigValue);
|
|
|
|
while (p + sizeof bigValue <= end) {
|
|
if (*((uint64 *) p) != bigValue) {
|
|
/* That's not right... let the loop below report the exact address. */
|
|
break;
|
|
}
|
|
|
|
size -= sizeof bigValue;
|
|
p += sizeof bigValue;
|
|
}
|
|
|
|
/* Handle any trailing bytes. */
|
|
while (p < end) {
|
|
if (*p != byteValue) {
|
|
return p;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* Util_BufferIsEmpty --
|
|
*
|
|
* Determine if the specified buffer of 'len' bytes starting at 'base'
|
|
* is empty (i.e. full of zeroes).
|
|
*
|
|
* Results:
|
|
* TRUE Yes
|
|
* FALSE No
|
|
*
|
|
* Side effects:
|
|
* None
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE Bool
|
|
Util_BufferIsEmpty(void const *base, // IN:
|
|
size_t len) // IN:
|
|
{
|
|
return Util_ValidateBytes(base, len, '\0') == NULL;
|
|
}
|
|
|
|
|
|
Bool Util_MakeSureDirExistsAndAccessible(char const *path,
|
|
unsigned int mode);
|
|
|
|
#if _WIN32
|
|
# define DIRSEPS "\\"
|
|
# define DIRSEPS_W L"\\"
|
|
# define DIRSEPC '\\'
|
|
# define DIRSEPC_W L'\\'
|
|
# define VALID_DIRSEPS "\\/"
|
|
# define VALID_DIRSEPS_W L"\\/"
|
|
# define CUR_DIRS_W L"."
|
|
# define CUR_DIRC_W L'.'
|
|
#else
|
|
# define DIRSEPS "/"
|
|
# define DIRSEPC '/'
|
|
# define VALID_DIRSEPS DIRSEPS
|
|
#endif
|
|
|
|
#define CURR_DIRS "."
|
|
#define CURR_DIRC '.'
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------
|
|
*
|
|
* Util_Safe[Malloc, Realloc, Calloc, Strdup] and
|
|
* Util_Safe[Malloc, Realloc, Calloc, Strdup]Bug --
|
|
*
|
|
* These functions work just like the standard C library functions
|
|
* (except Util_SafeStrdup[,Bug]() accept NULL, see below),
|
|
* but will not fail. Instead they Panic(), printing the file and
|
|
* line number of the caller, if the underlying library function
|
|
* fails. The Util_SafeFnBug functions print bugNumber in the
|
|
* Panic() message.
|
|
*
|
|
* These functions should only be used when there is no way to
|
|
* gracefully recover from the error condition.
|
|
*
|
|
* The internal versions of these functions expect a bug number
|
|
* as the first argument. If that bug number is something other
|
|
* than -1, the panic message will include the bug number.
|
|
*
|
|
* Since Util_SafeStrdup[,Bug]() do not need to return NULL
|
|
* on error, they have been extended to accept the null pointer
|
|
* (and return it). The competing view is that they should
|
|
* panic on NULL. This is a convenience vs. strictness argument.
|
|
* Convenience wins. -- edward
|
|
*
|
|
* Results:
|
|
* The freshly allocated memory.
|
|
*
|
|
* Side effects:
|
|
* Panic() if the library function fails.
|
|
*
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
|
|
void *UtilSafeMalloc0(size_t size);
|
|
void *UtilSafeMalloc1(size_t size,
|
|
int bugNumber, const char *file, int lineno);
|
|
|
|
void *UtilSafeRealloc0(void *ptr, size_t size);
|
|
void *UtilSafeRealloc1(void *ptr, size_t size,
|
|
int bugNumber, const char *file, int lineno);
|
|
|
|
void *UtilSafeCalloc0(size_t nmemb, size_t size);
|
|
void *UtilSafeCalloc1(size_t nmemb, size_t size,
|
|
int bugNumber, const char *file, int lineno);
|
|
|
|
char *UtilSafeStrdup0(const char *s);
|
|
char *UtilSafeStrdup1(const char *s,
|
|
int bugNumber, const char *file, int lineno);
|
|
|
|
char *UtilSafeStrndup0(const char *s, size_t n);
|
|
char *UtilSafeStrndup1(const char *s, size_t n,
|
|
int bugNumber, const char *file, int lineno);
|
|
|
|
/*
|
|
* Debug builds carry extra arguments into the allocation functions for
|
|
* better error reporting. Non-debug builds don't pay this extra overhead.
|
|
*/
|
|
#ifdef VMX86_DEBUG
|
|
|
|
#define Util_SafeMalloc(_size) \
|
|
UtilSafeMalloc1((_size), -1, __FILE__, __LINE__)
|
|
|
|
#define Util_SafeMallocBug(_bugNr, _size) \
|
|
UtilSafeMalloc1((_size),(_bugNr), __FILE__, __LINE__)
|
|
|
|
#define Util_SafeRealloc(_ptr, _size) \
|
|
UtilSafeRealloc1((_ptr), (_size), -1, __FILE__, __LINE__)
|
|
|
|
#define Util_SafeReallocBug(_bugNr, _ptr, _size) \
|
|
UtilSafeRealloc1((_ptr), (_size), (_bugNr), __FILE__, __LINE__)
|
|
|
|
#define Util_SafeCalloc(_nmemb, _size) \
|
|
UtilSafeCalloc1((_nmemb), (_size), -1, __FILE__, __LINE__)
|
|
|
|
#define Util_SafeCallocBug(_bugNr, _nmemb, _size) \
|
|
UtilSafeCalloc1((_nmemb), (_size), (_bugNr), __FILE__, __LINE__)
|
|
|
|
#define Util_SafeStrndup(_str, _size) \
|
|
UtilSafeStrndup1((_str), (_size), -1, __FILE__, __LINE__)
|
|
|
|
#define Util_SafeStrndupBug(_bugNr, _str, _size) \
|
|
UtilSafeStrndup1((_str), (_size), (_bugNr), __FILE__, __LINE__)
|
|
|
|
#define Util_SafeStrdup(_str) \
|
|
UtilSafeStrdup1((_str), -1, __FILE__, __LINE__)
|
|
|
|
#define Util_SafeStrdupBug(_bugNr, _str) \
|
|
UtilSafeStrdup1((_str), (_bugNr), __FILE__, __LINE__)
|
|
|
|
#else /* VMX86_DEBUG */
|
|
|
|
#define Util_SafeMalloc(_size) \
|
|
UtilSafeMalloc0((_size))
|
|
|
|
#define Util_SafeMallocBug(_bugNr, _size) \
|
|
UtilSafeMalloc0((_size))
|
|
|
|
#define Util_SafeRealloc(_ptr, _size) \
|
|
UtilSafeRealloc0((_ptr), (_size))
|
|
|
|
#define Util_SafeReallocBug(_ptr, _size) \
|
|
UtilSafeRealloc0((_ptr), (_size))
|
|
|
|
#define Util_SafeCalloc(_nmemb, _size) \
|
|
UtilSafeCalloc0((_nmemb), (_size))
|
|
|
|
#define Util_SafeCallocBug(_bugNr, _nmemb, _size) \
|
|
UtilSafeCalloc0((_nmemb), (_size))
|
|
|
|
#define Util_SafeStrndup(_str, _size) \
|
|
UtilSafeStrndup0((_str), (_size))
|
|
|
|
#define Util_SafeStrndupBug(_bugNr, _str, _size) \
|
|
UtilSafeStrndup0((_str), (_size))
|
|
|
|
#define Util_SafeStrdup(_str) \
|
|
UtilSafeStrdup0((_str))
|
|
|
|
#define Util_SafeStrdupBug(_bugNr, _str) \
|
|
UtilSafeStrdup0((_str))
|
|
|
|
#endif /* VMX86_DEBUG */
|
|
|
|
|
|
void *Util_Memcpy(void *dest, const void *src, size_t count);
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_Zero --
|
|
*
|
|
* Zeros out bufSize bytes of buf. NULL is legal.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
Util_Zero(void *buf, // OUT
|
|
size_t bufSize) // IN
|
|
{
|
|
if (buf != NULL) {
|
|
#if defined _WIN32 && defined USERLEVEL
|
|
/*
|
|
* Simple memset calls might be optimized out. See CERT advisory
|
|
* MSC06-C.
|
|
*/
|
|
SecureZeroMemory(buf, bufSize);
|
|
#else
|
|
memset(buf, 0, bufSize);
|
|
#if !defined _WIN32
|
|
/*
|
|
* Memset calls before free might be optimized out. See PR1248269.
|
|
*/
|
|
__asm__ __volatile__("" : : "r"(&buf) : "memory");
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_ZeroString --
|
|
*
|
|
* Zeros out a NULL-terminated string. NULL is legal.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
Util_ZeroString(char *str) // IN/OUT
|
|
{
|
|
if (str != NULL) {
|
|
Util_Zero(str, strlen(str));
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef VMKBOOT
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_ZeroFree --
|
|
*
|
|
* Zeros out bufSize bytes of buf, and then frees it. NULL is
|
|
* legal.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* buf is zeroed, and then free() is called on it.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
Util_ZeroFree(void *buf, // OUT
|
|
size_t bufSize) // IN
|
|
{
|
|
if (buf != NULL) {
|
|
Util_Zero(buf, bufSize);
|
|
free(buf);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_ZeroFreeString --
|
|
*
|
|
* Zeros out a NULL-terminated string, and then frees it. NULL is
|
|
* legal.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* str is zeroed, and then free() is called on it.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
Util_ZeroFreeString(char *str) // IN
|
|
{
|
|
if (str != NULL) {
|
|
Util_ZeroString(str);
|
|
free(str);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_ZeroFreeStringW --
|
|
*
|
|
* Zeros out a NUL-terminated wide-character string, and then frees it.
|
|
* NULL is legal.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* str is zeroed, and then free() is called on it.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
Util_ZeroFreeStringW(wchar_t *str) // IN
|
|
{
|
|
if (str != NULL) {
|
|
Util_Zero(str, wcslen(str) * sizeof *str);
|
|
free(str);
|
|
}
|
|
}
|
|
#endif // _WIN32
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_FreeList --
|
|
* Util_FreeStringList --
|
|
*
|
|
* Free a list (actually a vector) of allocated objects.
|
|
* The list (vector) itself is also freed.
|
|
*
|
|
* The list either has a specified length or is
|
|
* argv-style NULL terminated (if length is negative).
|
|
*
|
|
* The list can be NULL, in which case no operation is performed.
|
|
*
|
|
* Results:
|
|
* None
|
|
*
|
|
* Side effects:
|
|
* errno or Windows last error is preserved.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void
|
|
Util_FreeList(void **list, // IN/OUT: the list to free
|
|
ssize_t length) // IN: the length
|
|
{
|
|
if (list == NULL) {
|
|
ASSERT(length <= 0);
|
|
return;
|
|
}
|
|
|
|
if (length >= 0) {
|
|
ssize_t i;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
free(list[i]);
|
|
DEBUG_ONLY(list[i] = NULL);
|
|
}
|
|
} else {
|
|
void **s;
|
|
|
|
for (s = list; *s != NULL; s++) {
|
|
free(*s);
|
|
DEBUG_ONLY(*s = NULL);
|
|
}
|
|
}
|
|
free(list);
|
|
}
|
|
|
|
static INLINE void
|
|
Util_FreeStringList(char **list, // IN/OUT: the list to free
|
|
ssize_t length) // IN: the length
|
|
{
|
|
Util_FreeList((void **) list, length);
|
|
}
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_IsFileDescriptorOpen --
|
|
*
|
|
* Check if given file descriptor is open.
|
|
*
|
|
* Results:
|
|
* TRUE if fd is open.
|
|
*
|
|
* Side effects:
|
|
* Clobbers errno.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE Bool
|
|
Util_IsFileDescriptorOpen(int fd) // IN
|
|
{
|
|
return (lseek(fd, 0L, SEEK_CUR) == -1) ? errno != EBADF : TRUE;
|
|
}
|
|
#endif /* !_WIN32 */
|
|
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------------
|
|
*
|
|
* Util_Memcpy32 --
|
|
*
|
|
* Special purpose version of memcpy that requires nbytes be a
|
|
* multiple of 4. This assumption lets us have a very small,
|
|
* inlineable implementation.
|
|
*
|
|
* Results:
|
|
* dst
|
|
*
|
|
* Side effects:
|
|
* See above.
|
|
*
|
|
*-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static INLINE void *
|
|
Util_Memcpy32(void *dst, const void *src, size_t nbytes)
|
|
{
|
|
ASSERT((nbytes % 4) == 0);
|
|
#if defined __GNUC__ && (defined(__i386__) || defined(__x86_64__))
|
|
do {
|
|
int dummy0, dummy1, dummy2;
|
|
__asm__ __volatile__(
|
|
"cld \n\t"
|
|
"rep ; movsl" "\n\t"
|
|
: "=&c" (dummy0), "=&D" (dummy1), "=&S" (dummy2)
|
|
: "0" (nbytes / 4), "1" ((long) dst), "2" ((long) src)
|
|
: "memory", "cc"
|
|
);
|
|
return dst;
|
|
} while (0);
|
|
#else
|
|
return memcpy(dst, src, nbytes);
|
|
#endif
|
|
}
|
|
|
|
|
|
#endif /* UTIL_H */
|