mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 10:05:29 +00:00
Two were trivial. The one for strncmp is slightly trickier; you have to be careful with the signedness of chars when comparing them... but the code already casts s1 and s2 to (unsigned char*) before comparing them, so it's not a problem. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2618
359 lines
9.2 KiB
C
359 lines
9.2 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Replacements for strcpy(), memcpy() et al, which run on the ---*/
|
|
/*--- simulated CPU. ---*/
|
|
/*--- mac_replace_strmem.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of MemCheck, a heavyweight Valgrind tool for
|
|
detecting memory errors.
|
|
|
|
Copyright (C) 2000-2004 Julian Seward
|
|
jseward@acm.org
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any 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 GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#include "mc_include.h"
|
|
#include "memcheck.h"
|
|
#include "valgrind.h"
|
|
|
|
static Addr record_overlap_error;
|
|
|
|
static int init_done;
|
|
|
|
/* Startup hook - called as init section */
|
|
static void init(void) __attribute__((constructor));
|
|
static void init(void)
|
|
{
|
|
if (init_done)
|
|
return;
|
|
|
|
VALGRIND_MAGIC_SEQUENCE(record_overlap_error, 0,
|
|
_VG_USERREQ__MEMCHECK_GET_RECORD_OVERLAP,
|
|
0, 0, 0, 0);
|
|
init_done = 1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
The normal versions of these functions are hyper-optimised, which fools
|
|
Memcheck and cause spurious value warnings. So we replace them with
|
|
simpler versions. THEY RUN ON SIMD CPU!
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Figure out if [dst .. dst+dstlen-1] overlaps with
|
|
[src .. src+srclen-1].
|
|
We assume that the address ranges do not wrap around
|
|
(which is safe since on Linux addresses >= 0xC0000000
|
|
are not accessible and the program will segfault in this
|
|
circumstance, presumably).
|
|
*/
|
|
static __inline__
|
|
Bool is_overlap ( void* dst, const void* src, UInt dstlen, UInt srclen )
|
|
{
|
|
Addr loS, hiS, loD, hiD;
|
|
|
|
if (dstlen == 0 || srclen == 0)
|
|
return False;
|
|
|
|
loS = (Addr)src;
|
|
loD = (Addr)dst;
|
|
hiS = loS + srclen - 1;
|
|
hiD = loD + dstlen - 1;
|
|
|
|
/* So figure out if [loS .. hiS] overlaps with [loD .. hiD]. */
|
|
if (loS < loD) {
|
|
return !(hiS < loD);
|
|
}
|
|
else if (loD < loS) {
|
|
return !(hiD < loS);
|
|
}
|
|
else {
|
|
/* They start at same place. Since we know neither of them has
|
|
zero length, they must overlap. */
|
|
return True;
|
|
}
|
|
}
|
|
|
|
|
|
static __inline__
|
|
void complain2 ( Char* s, char* dst, const char* src )
|
|
{
|
|
OverlapExtra extra = {
|
|
.src = (Addr)src, .dst = (Addr)dst, .len = -1,
|
|
};
|
|
init();
|
|
VALGRIND_NON_SIMD_CALL2( record_overlap_error, s, &extra );
|
|
}
|
|
|
|
static __inline__
|
|
void complain3 ( Char* s, void* dst, const void* src, int n )
|
|
{
|
|
/* Must wrap it up here, because we cannot pass 4 args to core */
|
|
OverlapExtra extra = {
|
|
.src = (Addr)src, .dst = (Addr)dst, .len = n,
|
|
};
|
|
init();
|
|
VALGRIND_NON_SIMD_CALL2( record_overlap_error, s, &extra );
|
|
}
|
|
|
|
char* strrchr ( const char* s, int c )
|
|
{
|
|
UChar ch = (UChar)((UInt)c);
|
|
UChar* p = (UChar*)s;
|
|
UChar* last = NULL;
|
|
while (True) {
|
|
if (*p == ch) last = p;
|
|
if (*p == 0) return last;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
char* strchr ( const char* s, int c )
|
|
{
|
|
UChar ch = (UChar)((UInt)c);
|
|
UChar* p = (UChar*)s;
|
|
while (True) {
|
|
if (*p == ch) return p;
|
|
if (*p == 0) return NULL;
|
|
p++;
|
|
}
|
|
}
|
|
|
|
char* strcat ( char* dst, const char* src )
|
|
{
|
|
const Char* src_orig = src;
|
|
Char* dst_orig = dst;
|
|
while (*dst) dst++;
|
|
while (*src) *dst++ = *src++;
|
|
*dst = 0;
|
|
|
|
/* This is a bit redundant, I think; any overlap and the strcat will
|
|
go forever... or until a seg fault occurs. */
|
|
if (is_overlap(dst_orig,
|
|
src_orig,
|
|
(Addr)dst-(Addr)dst_orig+1,
|
|
(Addr)src-(Addr)src_orig+1))
|
|
complain2("strcat", dst_orig, src_orig);
|
|
|
|
return dst_orig;
|
|
}
|
|
|
|
char* strncat ( char* dst, const char* src, unsigned int n )
|
|
{
|
|
const Char* src_orig = src;
|
|
Char* dst_orig = dst;
|
|
UInt m = 0;
|
|
|
|
while (*dst) dst++;
|
|
while (m < n && *src) { m++; *dst++ = *src++; } /* concat <= n chars */
|
|
*dst = 0; /* always add null */
|
|
|
|
/* This checks for overlap after copying, unavoidable without
|
|
pre-counting lengths... should be ok */
|
|
if (is_overlap(dst_orig,
|
|
src_orig,
|
|
(Addr)dst-(Addr)dst_orig+1,
|
|
(Addr)src-(Addr)src_orig+1))
|
|
complain3("strncat", dst_orig, src_orig, n);
|
|
|
|
return dst_orig;
|
|
}
|
|
|
|
unsigned int strnlen ( const char* str, unsigned int n )
|
|
{
|
|
UInt i = 0;
|
|
while (i < n && str[i] != 0) i++;
|
|
return i;
|
|
}
|
|
|
|
unsigned int strlen ( const char* str )
|
|
{
|
|
UInt i = 0;
|
|
while (str[i] != 0) i++;
|
|
return i;
|
|
}
|
|
|
|
char* strcpy ( char* dst, const char* src )
|
|
{
|
|
const Char* src_orig = src;
|
|
Char* dst_orig = dst;
|
|
|
|
while (*src) *dst++ = *src++;
|
|
*dst = 0;
|
|
|
|
/* This checks for overlap after copying, unavoidable without
|
|
pre-counting length... should be ok */
|
|
if (is_overlap(dst_orig,
|
|
src_orig,
|
|
(Addr)dst-(Addr)dst_orig+1,
|
|
(Addr)src-(Addr)src_orig+1))
|
|
complain2("strcpy", dst_orig, src_orig);
|
|
|
|
return dst_orig;
|
|
}
|
|
|
|
char* strncpy ( char* dst, const char* src, unsigned int n )
|
|
{
|
|
const Char* src_orig = src;
|
|
Char* dst_orig = dst;
|
|
UInt m = 0;
|
|
|
|
while (m < n && *src) { m++; *dst++ = *src++; }
|
|
/* Check for overlap after copying; all n bytes of dst are relevant,
|
|
but only m+1 bytes of src if terminator was found */
|
|
if (is_overlap(dst_orig, src_orig, n, (m < n) ? m+1 : n))
|
|
complain3("strncpy", dst, src, n);
|
|
while (m++ < n) *dst++ = 0; /* must pad remainder with nulls */
|
|
|
|
return dst_orig;
|
|
}
|
|
|
|
int strncmp ( const char* s1, const char* s2, unsigned int nmax )
|
|
{
|
|
unsigned int n = 0;
|
|
while (True) {
|
|
if (n >= nmax) return 0;
|
|
if (*s1 == 0 && *s2 == 0) return 0;
|
|
if (*s1 == 0) return -1;
|
|
if (*s2 == 0) return 1;
|
|
|
|
if (*(unsigned char*)s1 < *(unsigned char*)s2) return -1;
|
|
if (*(unsigned char*)s1 > *(unsigned char*)s2) return 1;
|
|
|
|
s1++; s2++; n++;
|
|
}
|
|
}
|
|
|
|
int strcmp ( const char* s1, const char* s2 )
|
|
{
|
|
register unsigned char c1;
|
|
register unsigned char c2;
|
|
while (True) {
|
|
c1 = *(unsigned char *)s1;
|
|
c2 = *(unsigned char *)s2;
|
|
if (c1 != c2) break;
|
|
if (c1 == 0) break;
|
|
s1++; s2++;
|
|
}
|
|
if ((unsigned char)c1 < (unsigned char)c2) return -1;
|
|
if ((unsigned char)c1 > (unsigned char)c2) return 1;
|
|
return 0;
|
|
}
|
|
|
|
void* memchr(const void *s, int c, unsigned int n)
|
|
{
|
|
unsigned int i;
|
|
UChar c0 = (UChar)c;
|
|
UChar* p = (UChar*)s;
|
|
for (i = 0; i < n; i++)
|
|
if (p[i] == c0) return (void*)(&p[i]);
|
|
return NULL;
|
|
}
|
|
|
|
void* memcpy( void *dst, const void *src, unsigned int len )
|
|
{
|
|
register char *d;
|
|
register char *s;
|
|
|
|
if (len == 0)
|
|
return dst;
|
|
|
|
if (is_overlap(dst, src, len, len))
|
|
complain3("memcpy", dst, src, len);
|
|
|
|
if ( dst > src ) {
|
|
d = (char *)dst + len - 1;
|
|
s = (char *)src + len - 1;
|
|
while ( len >= 4 ) {
|
|
*d-- = *s--;
|
|
*d-- = *s--;
|
|
*d-- = *s--;
|
|
*d-- = *s--;
|
|
len -= 4;
|
|
}
|
|
while ( len-- ) {
|
|
*d-- = *s--;
|
|
}
|
|
} else if ( dst < src ) {
|
|
d = (char *)dst;
|
|
s = (char *)src;
|
|
while ( len >= 4 ) {
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
len -= 4;
|
|
}
|
|
while ( len-- ) {
|
|
*d++ = *s++;
|
|
}
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
int memcmp ( const void *s1V, const void *s2V, unsigned int n )
|
|
{
|
|
int res;
|
|
unsigned char a0;
|
|
unsigned char b0;
|
|
unsigned char* s1 = (unsigned char*)s1V;
|
|
unsigned char* s2 = (unsigned char*)s2V;
|
|
|
|
while (n != 0) {
|
|
a0 = s1[0];
|
|
b0 = s2[0];
|
|
s1 += 1;
|
|
s2 += 1;
|
|
res = ((int)a0) - ((int)b0);
|
|
if (res != 0)
|
|
return res;
|
|
n -= 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Copy SRC to DEST, returning the address of the terminating '\0' in
|
|
DEST. (minor variant of strcpy) */
|
|
|
|
char* stpcpy ( char* dst, const char* src )
|
|
{
|
|
const Char* src_orig = src;
|
|
Char* dst_orig = dst;
|
|
|
|
while (*src) *dst++ = *src++;
|
|
*dst = 0;
|
|
|
|
/* This checks for overlap after copying, unavoidable without
|
|
pre-counting length... should be ok */
|
|
if (is_overlap(dst_orig,
|
|
src_orig,
|
|
(Addr)dst-(Addr)dst_orig+1,
|
|
(Addr)src-(Addr)src_orig+1))
|
|
complain2("stpcpy", dst_orig, src_orig);
|
|
|
|
return dst;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end mac_replace_strmem.c ---*/
|
|
/*--------------------------------------------------------------------*/
|