mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-09 05:10:23 +00:00
throw away the old signals simulation and more or less start again from scratch. vg_signals.c is nearly a complete rewrite. In fact this is now the third generation of the signals simulation. The purpose of this is to properly support signals in threads -- a nightmare combination. pthread_sigmask, pthread_kill and sigwait are now alledged to work as POSIX requires. In the process, throw away confusing and conceptually muddled old implementation and replace with something which is more verbose but conceptually cleaner, simpler and easier to argue is correct. * When the client does sigaction/sigprocmask et al, the resulting changes are stored verbatim in SCSS -- the Static Client Signal State. So SCSS is the state the client believes the kernel is in. * Every time SCSS changes, we recalculate the state the kernel *should* be in so that our signal simulation works. This is the SKSS -- Static Kernel Signal State. The kernel state is then updated accordingly. By diffing the new and old SKSSs, the number of real system calls made is minimised. * The dynamic state of the client's signals is stored in DCSS -- Dynamic Client Signal State. This just records which signals are pending for which threads. The big advantage of this scheme over the previous is that the SCSS -> SKSS mapping is made explicit and gathered all in one place, rather than spread out in a confusing way and done implicitly. That makes it all lot easier to decide if the mapping, which is really the heart of the signals simulation, is correct or not. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@271
1243 lines
32 KiB
C
1243 lines
32 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Reimplementation of some C library stuff, to avoid depending ---*/
|
|
/*--- on libc.so. ---*/
|
|
/*--- vg_mylibc.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, an x86 protected-mode emulator
|
|
designed for debugging and profiling binaries on x86-Unixes.
|
|
|
|
Copyright (C) 2000-2002 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 LICENSE.
|
|
*/
|
|
|
|
#include "vg_include.h"
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Really Actually DO system calls.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Ripped off from /usr/include/asm/unistd.h. */
|
|
|
|
static
|
|
UInt vg_do_syscall0 ( UInt syscallno )
|
|
{
|
|
UInt __res;
|
|
__asm__ volatile ("int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (syscallno) );
|
|
return __res;
|
|
}
|
|
|
|
|
|
static
|
|
UInt vg_do_syscall1 ( UInt syscallno, UInt arg1 )
|
|
{
|
|
UInt __res;
|
|
__asm__ volatile ("int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (syscallno),
|
|
"b" (arg1) );
|
|
return __res;
|
|
}
|
|
|
|
|
|
static
|
|
UInt vg_do_syscall2 ( UInt syscallno,
|
|
UInt arg1, UInt arg2 )
|
|
{
|
|
UInt __res;
|
|
__asm__ volatile ("int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (syscallno),
|
|
"b" (arg1),
|
|
"c" (arg2) );
|
|
return __res;
|
|
}
|
|
|
|
|
|
static
|
|
UInt vg_do_syscall3 ( UInt syscallno,
|
|
UInt arg1, UInt arg2, UInt arg3 )
|
|
{
|
|
UInt __res;
|
|
__asm__ volatile ("int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (syscallno),
|
|
"b" (arg1),
|
|
"c" (arg2),
|
|
"d" (arg3) );
|
|
return __res;
|
|
}
|
|
|
|
|
|
static
|
|
UInt vg_do_syscall4 ( UInt syscallno,
|
|
UInt arg1, UInt arg2, UInt arg3, UInt arg4 )
|
|
{
|
|
UInt __res;
|
|
__asm__ volatile ("int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (syscallno),
|
|
"b" (arg1),
|
|
"c" (arg2),
|
|
"d" (arg3),
|
|
"S" (arg4) );
|
|
return __res;
|
|
}
|
|
|
|
|
|
#if 0
|
|
static
|
|
UInt vg_do_syscall5 ( UInt syscallno,
|
|
UInt arg1, UInt arg2, UInt arg3, UInt arg4,
|
|
UInt arg5 )
|
|
{
|
|
UInt __res;
|
|
__asm__ volatile ("int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (syscallno),
|
|
"b" (arg1),
|
|
"c" (arg2),
|
|
"d" (arg3),
|
|
"S" (arg4),
|
|
"D" (arg5) );
|
|
return __res;
|
|
}
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Wrappers around system calls, and other stuff, to do with signals.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* sigemptyset, sigfullset, sigaddset and sigdelset return 0 on
|
|
success and -1 on error.
|
|
*/
|
|
Int VG_(ksigfillset)( vki_ksigset_t* set )
|
|
{
|
|
Int i;
|
|
if (set == NULL)
|
|
return -1;
|
|
for (i = 0; i < VKI_KNSIG_WORDS; i++)
|
|
set->ws[i] = 0xFFFFFFFF;
|
|
return 0;
|
|
}
|
|
|
|
Int VG_(ksigemptyset)( vki_ksigset_t* set )
|
|
{
|
|
Int i;
|
|
if (set == NULL)
|
|
return -1;
|
|
for (i = 0; i < VKI_KNSIG_WORDS; i++)
|
|
set->ws[i] = 0x0;
|
|
return 0;
|
|
}
|
|
|
|
Bool VG_(kisemptysigset)( vki_ksigset_t* set )
|
|
{
|
|
Int i;
|
|
vg_assert(set != NULL);
|
|
for (i = 0; i < VKI_KNSIG_WORDS; i++)
|
|
if (set->ws[i] != 0x0) return False;
|
|
return True;
|
|
}
|
|
|
|
Bool VG_(kisfullsigset)( vki_ksigset_t* set )
|
|
{
|
|
Int i;
|
|
vg_assert(set != NULL);
|
|
for (i = 0; i < VKI_KNSIG_WORDS; i++)
|
|
if (set->ws[i] != ~0x0) return False;
|
|
return True;
|
|
}
|
|
|
|
|
|
Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
|
|
{
|
|
if (set == NULL)
|
|
return -1;
|
|
if (signum < 1 && signum > VKI_KNSIG)
|
|
return -1;
|
|
signum--;
|
|
set->ws[signum / VKI_KNSIG_BPW] |= (1 << (signum % VKI_KNSIG_BPW));
|
|
return 0;
|
|
}
|
|
|
|
Int VG_(ksigdelset)( vki_ksigset_t* set, Int signum )
|
|
{
|
|
if (set == NULL)
|
|
return -1;
|
|
if (signum < 1 && signum > VKI_KNSIG)
|
|
return -1;
|
|
signum--;
|
|
set->ws[signum / VKI_KNSIG_BPW] &= ~(1 << (signum % VKI_KNSIG_BPW));
|
|
return 0;
|
|
}
|
|
|
|
Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
|
|
{
|
|
if (set == NULL)
|
|
return 0;
|
|
if (signum < 1 && signum > VKI_KNSIG)
|
|
return 0;
|
|
signum--;
|
|
if (1 & ((set->ws[signum / VKI_KNSIG_BPW]) >> (signum % VKI_KNSIG_BPW)))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Add all signals in src to dst. */
|
|
void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
|
|
{
|
|
Int i;
|
|
vg_assert(dst != NULL && src != NULL);
|
|
for (i = 0; i < VKI_KNSIG_WORDS; i++)
|
|
dst->ws[i] |= src->ws[i];
|
|
}
|
|
|
|
/* Remove all signals in src from dst. */
|
|
void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
|
|
{
|
|
Int i;
|
|
vg_assert(dst != NULL && src != NULL);
|
|
for (i = 0; i < VKI_KNSIG_WORDS; i++)
|
|
dst->ws[i] &= ~(src->ws[i]);
|
|
}
|
|
|
|
|
|
/* The functions sigaction, sigprocmask, sigpending and sigsuspend
|
|
return 0 on success and -1 on error.
|
|
*/
|
|
Int VG_(ksigprocmask)( Int how,
|
|
const vki_ksigset_t* set,
|
|
vki_ksigset_t* oldset)
|
|
{
|
|
Int res
|
|
= vg_do_syscall4(__NR_rt_sigprocmask,
|
|
how, (UInt)set, (UInt)oldset,
|
|
VKI_KNSIG_WORDS * VKI_BYTES_PER_WORD);
|
|
return VG_(is_kerror)(res) ? -1 : 0;
|
|
}
|
|
|
|
|
|
Int VG_(ksigaction) ( Int signum,
|
|
const vki_ksigaction* act,
|
|
vki_ksigaction* oldact)
|
|
{
|
|
Int res
|
|
= vg_do_syscall4(__NR_rt_sigaction,
|
|
signum, (UInt)act, (UInt)oldact,
|
|
VKI_KNSIG_WORDS * VKI_BYTES_PER_WORD);
|
|
/* VG_(printf)("res = %d\n",res); */
|
|
return VG_(is_kerror)(res) ? -1 : 0;
|
|
}
|
|
|
|
|
|
Int VG_(ksigaltstack)( const vki_kstack_t* ss, vki_kstack_t* oss )
|
|
{
|
|
Int res
|
|
= vg_do_syscall2(__NR_sigaltstack, (UInt)ss, (UInt)oss);
|
|
return VG_(is_kerror)(res) ? -1 : 0;
|
|
}
|
|
|
|
|
|
Int VG_(ksignal)(Int signum, void (*sighandler)(Int))
|
|
{
|
|
Int res;
|
|
vki_ksigaction sa;
|
|
sa.ksa_handler = sighandler;
|
|
sa.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
|
|
sa.ksa_restorer = NULL;
|
|
res = VG_(ksigemptyset)( &sa.ksa_mask );
|
|
vg_assert(res == 0);
|
|
res = vg_do_syscall4(__NR_rt_sigaction,
|
|
signum, (UInt)(&sa), (UInt)NULL,
|
|
VKI_KNSIG_WORDS * VKI_BYTES_PER_WORD);
|
|
return VG_(is_kerror)(res) ? -1 : 0;
|
|
}
|
|
|
|
|
|
Int VG_(kill)( Int pid, Int signo )
|
|
{
|
|
Int res = vg_do_syscall2(__NR_kill, pid, signo);
|
|
return VG_(is_kerror)(res) ? -1 : 0;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
mmap/munmap, exit, fcntl
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Returns -1 on failure. */
|
|
void* VG_(mmap)( void* start, UInt length,
|
|
UInt prot, UInt flags, UInt fd, UInt offset)
|
|
{
|
|
Int res;
|
|
UInt args[6];
|
|
args[0] = (UInt)start;
|
|
args[1] = length;
|
|
args[2] = prot;
|
|
args[3] = flags;
|
|
args[4] = fd;
|
|
args[5] = offset;
|
|
res = vg_do_syscall1(__NR_mmap, (UInt)(&(args[0])) );
|
|
return VG_(is_kerror)(res) ? ((void*)(-1)) : (void*)res;
|
|
}
|
|
|
|
/* Returns -1 on failure. */
|
|
Int VG_(munmap)( void* start, Int length )
|
|
{
|
|
Int res = vg_do_syscall2(__NR_munmap, (UInt)start, (UInt)length );
|
|
return VG_(is_kerror)(res) ? -1 : 0;
|
|
}
|
|
|
|
void VG_(exit)( Int status )
|
|
{
|
|
(void)vg_do_syscall1(__NR_exit, (UInt)status );
|
|
/* Why are we still alive here? */
|
|
/*NOTREACHED*/
|
|
vg_assert(2+2 == 5);
|
|
}
|
|
|
|
/* Returns -1 on error. */
|
|
Int VG_(fcntl) ( Int fd, Int cmd, Int arg )
|
|
{
|
|
Int res = vg_do_syscall3(__NR_fcntl, fd, cmd, arg);
|
|
return VG_(is_kerror)(res) ? -1 : res;
|
|
}
|
|
|
|
/* Returns -1 on error. */
|
|
Int VG_(select)( Int n,
|
|
vki_fd_set* readfds,
|
|
vki_fd_set* writefds,
|
|
vki_fd_set* exceptfds,
|
|
struct vki_timeval * timeout )
|
|
{
|
|
Int res;
|
|
UInt args[5];
|
|
args[0] = n;
|
|
args[1] = (UInt)readfds;
|
|
args[2] = (UInt)writefds;
|
|
args[3] = (UInt)exceptfds;
|
|
args[4] = (UInt)timeout;
|
|
res = vg_do_syscall1(__NR_select, (UInt)(&(args[0])) );
|
|
return VG_(is_kerror)(res) ? -1 : res;
|
|
}
|
|
|
|
/* Returns -1 on error, 0 if ok, 1 if interrupted. */
|
|
Int VG_(nanosleep)( const struct vki_timespec *req,
|
|
struct vki_timespec *rem )
|
|
{
|
|
Int res;
|
|
res = vg_do_syscall2(__NR_nanosleep, (UInt)req, (UInt)rem);
|
|
if (res == -VKI_EINVAL) return -1;
|
|
if (res == -VKI_EINTR) return 1;
|
|
return 0;
|
|
}
|
|
|
|
void* VG_(brk) ( void* end_data_segment )
|
|
{
|
|
Int res;
|
|
res = vg_do_syscall1(__NR_brk, (UInt)end_data_segment);
|
|
return (void*)( VG_(is_kerror)(res) ? -1 : res );
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
printf implementation. The key function, vg_vprintf(), emits chars
|
|
into a caller-supplied function. Distantly derived from:
|
|
|
|
vprintf replacement for Checker.
|
|
Copyright 1993, 1994, 1995 Tristan Gingold
|
|
Written September 1993 Tristan Gingold
|
|
Tristan Gingold, 8 rue Parmentier, F-91120 PALAISEAU, FRANCE
|
|
|
|
(Checker itself was GPL'd.)
|
|
------------------------------------------------------------------ */
|
|
|
|
|
|
/* Some flags. */
|
|
#define VG_MSG_SIGNED 1 /* The value is signed. */
|
|
#define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */
|
|
#define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */
|
|
|
|
|
|
/* Copy a string into the buffer. */
|
|
static void
|
|
myvprintf_str ( void(*send)(Char), Int flags, Int width, Char* str,
|
|
Bool capitalise )
|
|
{
|
|
# define MAYBE_TOUPPER(ch) (capitalise ? VG_(toupper)(ch) : (ch))
|
|
|
|
Int i, extra;
|
|
Int len = VG_(strlen)(str);
|
|
|
|
if (width == 0) {
|
|
for (i = 0; i < len; i++)
|
|
send(MAYBE_TOUPPER(str[i]));
|
|
return;
|
|
}
|
|
|
|
if (len > width) {
|
|
for (i = 0; i < width; i++)
|
|
send(MAYBE_TOUPPER(str[i]));
|
|
return;
|
|
}
|
|
|
|
extra = width - len;
|
|
if (flags & VG_MSG_LJUSTIFY) {
|
|
for (i = 0; i < extra; i++)
|
|
send(' ');
|
|
}
|
|
for (i = 0; i < len; i++)
|
|
send(MAYBE_TOUPPER(str[i]));
|
|
if (!(flags & VG_MSG_LJUSTIFY)) {
|
|
for (i = 0; i < extra; i++)
|
|
send(' ');
|
|
}
|
|
|
|
# undef MAYBE_TOUPPER
|
|
}
|
|
|
|
/* Write P into the buffer according to these args:
|
|
* If SIGN is true, p is a signed.
|
|
* BASE is the base.
|
|
* If WITH_ZERO is true, '0' must be added.
|
|
* WIDTH is the width of the field.
|
|
*/
|
|
static void
|
|
myvprintf_int64 ( void(*send)(Char), Int flags, Int base, Int width, ULong p)
|
|
{
|
|
Char buf[40];
|
|
Int ind = 0;
|
|
Int i;
|
|
Bool neg = False;
|
|
Char *digits = "0123456789ABCDEF";
|
|
|
|
if (base < 2 || base > 16)
|
|
return;
|
|
|
|
if ((flags & VG_MSG_SIGNED) && (Long)p < 0) {
|
|
p = - (Long)p;
|
|
neg = True;
|
|
}
|
|
|
|
if (p == 0)
|
|
buf[ind++] = '0';
|
|
else {
|
|
while (p > 0) {
|
|
buf[ind++] = digits[p % base];
|
|
p /= base;
|
|
}
|
|
}
|
|
|
|
if (neg)
|
|
buf[ind++] = '-';
|
|
|
|
if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
|
|
for(; ind < width; ind++) {
|
|
vg_assert(ind < 39);
|
|
buf[ind] = (flags & VG_MSG_ZJUSTIFY) ? '0': ' ';
|
|
}
|
|
}
|
|
|
|
/* Reverse copy to buffer. */
|
|
for (i = ind -1; i >= 0; i--)
|
|
send(buf[i]);
|
|
|
|
if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
|
|
for(; ind < width; ind++)
|
|
send((flags & VG_MSG_ZJUSTIFY) ? '0': ' ');
|
|
}
|
|
}
|
|
|
|
|
|
/* A simple vprintf(). */
|
|
void
|
|
VG_(vprintf) ( void(*send)(Char), const Char *format, va_list vargs )
|
|
{
|
|
int i;
|
|
int flags;
|
|
int width;
|
|
Bool is_long;
|
|
|
|
/* We assume that vargs has already been initialised by the
|
|
caller, using va_start, and that the caller will similarly
|
|
clean up with va_end.
|
|
*/
|
|
|
|
for (i = 0; format[i] != 0; i++) {
|
|
if (format[i] != '%') {
|
|
send(format[i]);
|
|
continue;
|
|
}
|
|
i++;
|
|
/* A '%' has been found. Ignore a trailing %. */
|
|
if (format[i] == 0)
|
|
break;
|
|
if (format[i] == '%') {
|
|
/* `%%' is replaced by `%'. */
|
|
send('%');
|
|
continue;
|
|
}
|
|
flags = 0;
|
|
is_long = False;
|
|
width = 0; /* length of the field. */
|
|
/* If '-' follows '%', justify on the left. */
|
|
if (format[i] == '-') {
|
|
flags |= VG_MSG_LJUSTIFY;
|
|
i++;
|
|
}
|
|
/* If '0' follows '%', pads will be inserted. */
|
|
if (format[i] == '0') {
|
|
flags |= VG_MSG_ZJUSTIFY;
|
|
i++;
|
|
}
|
|
/* Compute the field length. */
|
|
while (format[i] >= '0' && format[i] <= '9') {
|
|
width *= 10;
|
|
width += format[i++] - '0';
|
|
}
|
|
while (format[i] == 'l') {
|
|
i++;
|
|
is_long = True;
|
|
}
|
|
|
|
switch (format[i]) {
|
|
case 'd': /* %d */
|
|
flags |= VG_MSG_SIGNED;
|
|
if (is_long)
|
|
myvprintf_int64(send, flags, 10, width,
|
|
(ULong)(va_arg (vargs, Long)));
|
|
else
|
|
myvprintf_int64(send, flags, 10, width,
|
|
(ULong)(va_arg (vargs, Int)));
|
|
break;
|
|
case 'u': /* %u */
|
|
if (is_long)
|
|
myvprintf_int64(send, flags, 10, width,
|
|
(ULong)(va_arg (vargs, ULong)));
|
|
else
|
|
myvprintf_int64(send, flags, 10, width,
|
|
(ULong)(va_arg (vargs, UInt)));
|
|
break;
|
|
case 'p': /* %p */
|
|
send('0');
|
|
send('x');
|
|
myvprintf_int64(send, flags, 16, width,
|
|
(ULong)((UInt)va_arg (vargs, void *)));
|
|
break;
|
|
case 'x': /* %x */
|
|
if (is_long)
|
|
myvprintf_int64(send, flags, 16, width,
|
|
(ULong)(va_arg (vargs, ULong)));
|
|
else
|
|
myvprintf_int64(send, flags, 16, width,
|
|
(ULong)(va_arg (vargs, UInt)));
|
|
break;
|
|
case 'c': /* %c */
|
|
send(va_arg (vargs, int));
|
|
break;
|
|
case 's': case 'S': { /* %s */
|
|
char *str = va_arg (vargs, char *);
|
|
if (str == (char*) 0) str = "(null)";
|
|
myvprintf_str(send, flags, width, str, format[i]=='S');
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* A general replacement for printf(). Note that only low-level
|
|
debugging info should be sent via here. The official route is to
|
|
to use vg_message(). This interface is deprecated.
|
|
*/
|
|
static char myprintf_buf[100];
|
|
static int n_myprintf_buf;
|
|
|
|
static void add_to_myprintf_buf ( Char c )
|
|
{
|
|
if (n_myprintf_buf >= 100-10 /*paranoia*/ ) {
|
|
if (VG_(clo_logfile_fd) >= 0)
|
|
VG_(write)
|
|
(VG_(clo_logfile_fd), myprintf_buf, VG_(strlen)(myprintf_buf));
|
|
n_myprintf_buf = 0;
|
|
myprintf_buf[n_myprintf_buf] = 0;
|
|
}
|
|
myprintf_buf[n_myprintf_buf++] = c;
|
|
myprintf_buf[n_myprintf_buf] = 0;
|
|
}
|
|
|
|
void VG_(printf) ( const char *format, ... )
|
|
{
|
|
va_list vargs;
|
|
va_start(vargs,format);
|
|
|
|
n_myprintf_buf = 0;
|
|
myprintf_buf[n_myprintf_buf] = 0;
|
|
VG_(vprintf) ( add_to_myprintf_buf, format, vargs );
|
|
|
|
if (n_myprintf_buf > 0 && VG_(clo_logfile_fd) >= 0)
|
|
VG_(write)
|
|
( VG_(clo_logfile_fd), myprintf_buf, VG_(strlen)(myprintf_buf));
|
|
|
|
va_end(vargs);
|
|
}
|
|
|
|
|
|
/* A general replacement for sprintf(). */
|
|
static Char* vg_sprintf_ptr;
|
|
|
|
static void add_to_vg_sprintf_buf ( Char c )
|
|
{
|
|
*vg_sprintf_ptr++ = c;
|
|
}
|
|
|
|
void VG_(sprintf) ( Char* buf, Char *format, ... )
|
|
{
|
|
va_list vargs;
|
|
va_start(vargs,format);
|
|
|
|
vg_sprintf_ptr = buf;
|
|
VG_(vprintf) ( add_to_vg_sprintf_buf, format, vargs );
|
|
add_to_vg_sprintf_buf(0);
|
|
|
|
va_end(vargs);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Misc str* functions.
|
|
------------------------------------------------------------------ */
|
|
|
|
Bool VG_(isspace) ( Char c )
|
|
{
|
|
return (c == ' ' || c == '\n' || c == '\t' || c == 0);
|
|
}
|
|
|
|
|
|
Int VG_(strlen) ( const Char* str )
|
|
{
|
|
Int i = 0;
|
|
while (str[i] != 0) i++;
|
|
return i;
|
|
}
|
|
|
|
|
|
Long VG_(atoll) ( Char* str )
|
|
{
|
|
Bool neg = False;
|
|
Long n = 0;
|
|
if (*str == '-') { str++; neg = True; };
|
|
while (*str >= '0' && *str <= '9') {
|
|
n = 10*n + (Long)(*str - '0');
|
|
str++;
|
|
}
|
|
if (neg) n = -n;
|
|
return n;
|
|
}
|
|
|
|
|
|
Char* VG_(strcat) ( Char* dest, const Char* src )
|
|
{
|
|
Char* dest_orig = dest;
|
|
while (*dest) dest++;
|
|
while (*src) *dest++ = *src++;
|
|
*dest = 0;
|
|
return dest_orig;
|
|
}
|
|
|
|
|
|
Char* VG_(strncat) ( Char* dest, const Char* src, Int n )
|
|
{
|
|
Char* dest_orig = dest;
|
|
while (*dest) dest++;
|
|
while (*src && n > 0) { *dest++ = *src++; n--; }
|
|
*dest = 0;
|
|
return dest_orig;
|
|
}
|
|
|
|
|
|
Char* VG_(strpbrk) ( const Char* s, const Char* accept )
|
|
{
|
|
const Char* a;
|
|
while (*s) {
|
|
a = accept;
|
|
while (*a)
|
|
if (*a++ == *s)
|
|
return (Char *) s;
|
|
s++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Char* VG_(strcpy) ( Char* dest, const Char* src )
|
|
{
|
|
Char* dest_orig = dest;
|
|
while (*src) *dest++ = *src++;
|
|
*dest = 0;
|
|
return dest_orig;
|
|
}
|
|
|
|
|
|
/* Copy bytes, not overrunning the end of dest and always ensuring
|
|
zero termination. */
|
|
void VG_(strncpy_safely) ( Char* dest, const Char* src, Int ndest )
|
|
{
|
|
Int i;
|
|
vg_assert(ndest > 0);
|
|
i = 0;
|
|
dest[i] = 0;
|
|
while (True) {
|
|
if (src[i] == 0) return;
|
|
if (i >= ndest-1) return;
|
|
dest[i] = src[i];
|
|
i++;
|
|
dest[i] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void VG_(strncpy) ( Char* dest, const Char* src, Int ndest )
|
|
{
|
|
VG_(strncpy_safely)( dest, src, ndest+1 );
|
|
}
|
|
|
|
|
|
Int VG_(strcmp) ( const Char* s1, const Char* s2 )
|
|
{
|
|
while (True) {
|
|
if (*s1 == 0 && *s2 == 0) return 0;
|
|
if (*s1 == 0) return -1;
|
|
if (*s2 == 0) return 1;
|
|
|
|
if (*(UChar*)s1 < *(UChar*)s2) return -1;
|
|
if (*(UChar*)s1 > *(UChar*)s2) return 1;
|
|
|
|
s1++; s2++;
|
|
}
|
|
}
|
|
|
|
|
|
Int VG_(strcmp_ws) ( const Char* s1, const Char* s2 )
|
|
{
|
|
while (True) {
|
|
if (VG_(isspace)(*s1) && VG_(isspace)(*s2)) return 0;
|
|
if (VG_(isspace)(*s1)) return -1;
|
|
if (VG_(isspace)(*s2)) return 1;
|
|
|
|
if (*(UChar*)s1 < *(UChar*)s2) return -1;
|
|
if (*(UChar*)s1 > *(UChar*)s2) return 1;
|
|
|
|
s1++; s2++;
|
|
}
|
|
}
|
|
|
|
|
|
Int VG_(strncmp) ( const Char* s1, const Char* s2, Int nmax )
|
|
{
|
|
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 (*(UChar*)s1 < *(UChar*)s2) return -1;
|
|
if (*(UChar*)s1 > *(UChar*)s2) return 1;
|
|
|
|
s1++; s2++; n++;
|
|
}
|
|
}
|
|
|
|
|
|
Int VG_(strncmp_ws) ( const Char* s1, const Char* s2, Int nmax )
|
|
{
|
|
Int n = 0;
|
|
while (True) {
|
|
if (n >= nmax) return 0;
|
|
if (VG_(isspace)(*s1) && VG_(isspace)(*s2)) return 0;
|
|
if (VG_(isspace)(*s1)) return -1;
|
|
if (VG_(isspace)(*s2)) return 1;
|
|
|
|
if (*(UChar*)s1 < *(UChar*)s2) return -1;
|
|
if (*(UChar*)s1 > *(UChar*)s2) return 1;
|
|
|
|
s1++; s2++; n++;
|
|
}
|
|
}
|
|
|
|
|
|
Char* VG_(strstr) ( const Char* haystack, Char* needle )
|
|
{
|
|
Int n;
|
|
if (haystack == NULL)
|
|
return NULL;
|
|
n = VG_(strlen)(needle);
|
|
while (True) {
|
|
if (haystack[0] == 0)
|
|
return NULL;
|
|
if (VG_(strncmp)(haystack, needle, n) == 0)
|
|
return (Char*)haystack;
|
|
haystack++;
|
|
}
|
|
}
|
|
|
|
|
|
Char* VG_(strchr) ( const Char* s, Char c )
|
|
{
|
|
while (True) {
|
|
if (*s == c) return (Char*)s;
|
|
if (*s == 0) return NULL;
|
|
s++;
|
|
}
|
|
}
|
|
|
|
|
|
Char VG_(toupper) ( Char c )
|
|
{
|
|
if (c >= 'a' && c <= 'z')
|
|
return c + ('A' - 'a');
|
|
else
|
|
return c;
|
|
}
|
|
|
|
|
|
Char* VG_(strdup) ( ArenaId aid, const Char* s )
|
|
{
|
|
Int i;
|
|
Int len = VG_(strlen)(s) + 1;
|
|
Char* res = VG_(malloc) (aid, len);
|
|
for (i = 0; i < len; i++)
|
|
res[i] = s[i];
|
|
return res;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
A simple string matching routine, purloined from Hugs98.
|
|
`*' matches any sequence of zero or more characters
|
|
`?' matches any single character exactly
|
|
`\c' matches the character c only (ignoring special chars)
|
|
c matches the character c only
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Keep track of recursion depth. */
|
|
static Int recDepth;
|
|
|
|
static Bool stringMatch_wrk ( Char* pat, Char* str )
|
|
{
|
|
vg_assert(recDepth >= 0 && recDepth < 500);
|
|
recDepth++;
|
|
for (;;) {
|
|
switch (*pat) {
|
|
case '\0' : return (*str=='\0');
|
|
case '*' : do {
|
|
if (stringMatch_wrk(pat+1,str)) {
|
|
recDepth--;
|
|
return True;
|
|
}
|
|
} while (*str++);
|
|
recDepth--;
|
|
return False;
|
|
case '?' : if (*str++=='\0') {
|
|
recDepth--;
|
|
return False;
|
|
}
|
|
pat++;
|
|
break;
|
|
case '\\' : if (*++pat == '\0') {
|
|
recDepth--;
|
|
return False; /* spurious trailing \ in pattern */
|
|
}
|
|
/* falls through to ... */
|
|
default : if (*pat++ != *str++) {
|
|
recDepth--;
|
|
return False;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Bool VG_(stringMatch) ( Char* pat, Char* str )
|
|
{
|
|
Bool b;
|
|
recDepth = 0;
|
|
b = stringMatch_wrk ( pat, str );
|
|
/*
|
|
VG_(printf)("%s %s %s\n",
|
|
b?"TRUE ":"FALSE", pat, str);
|
|
*/
|
|
return b;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Assertery.
|
|
------------------------------------------------------------------ */
|
|
|
|
#define EMAIL_ADDR "jseward@acm.org"
|
|
|
|
void VG_(assert_fail) ( Char* expr, Char* file, Int line, Char* fn )
|
|
{
|
|
static Bool entered = False;
|
|
if (entered)
|
|
VG_(exit)(2);
|
|
entered = True;
|
|
VG_(printf)("\n%s: %s:%d (%s): Assertion `%s' failed.\n",
|
|
"valgrind", file, line, fn, expr );
|
|
VG_(pp_sched_status)();
|
|
VG_(printf)("Please report this bug to me at: %s\n\n", EMAIL_ADDR);
|
|
VG_(shutdown_logging)();
|
|
VG_(exit)(1);
|
|
}
|
|
|
|
void VG_(panic) ( Char* str )
|
|
{
|
|
VG_(printf)("\nvalgrind: the `impossible' happened:\n %s\n", str);
|
|
VG_(printf)("Basic block ctr is approximately %llu\n", VG_(bbs_done) );
|
|
VG_(pp_sched_status)();
|
|
VG_(printf)("Please report this bug to me at: %s\n\n", EMAIL_ADDR);
|
|
VG_(shutdown_logging)();
|
|
VG_(exit)(1);
|
|
}
|
|
|
|
#undef EMAIL_ADDR
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Primitive support for reading files.
|
|
------------------------------------------------------------------ */
|
|
|
|
/* Returns -1 on failure. */
|
|
Int VG_(open_read) ( Char* pathname )
|
|
{
|
|
Int fd;
|
|
/* VG_(printf)("vg_open_read %s\n", pathname ); */
|
|
|
|
/* This gets a segmentation fault if pathname isn't a valid file.
|
|
I don't know why. It seems like the call to open is getting
|
|
intercepted and messed with by glibc ... */
|
|
/* fd = open( pathname, O_RDONLY ); */
|
|
/* ... so we go direct to the horse's mouth, which seems to work
|
|
ok: */
|
|
const int O_RDONLY = 0; /* See /usr/include/bits/fcntl.h */
|
|
fd = vg_do_syscall3(__NR_open, (UInt)pathname, O_RDONLY, 0);
|
|
/* VG_(printf)("result = %d\n", fd); */
|
|
if (VG_(is_kerror)(fd)) fd = -1;
|
|
return fd;
|
|
}
|
|
|
|
/* Returns -1 on failure. */
|
|
static Int VG_(chmod_u_rw) ( Int fd )
|
|
{
|
|
Int res;
|
|
const int O_IRUSR_IWUSR = 000600; /* See /usr/include/cpio.h */
|
|
res = vg_do_syscall2(__NR_fchmod, fd, O_IRUSR_IWUSR);
|
|
if (VG_(is_kerror)(res)) res = -1;
|
|
return res;
|
|
}
|
|
|
|
/* Returns -1 on failure. */
|
|
Int VG_(create_and_write) ( Char* pathname )
|
|
{
|
|
Int fd;
|
|
|
|
const int O_CR_AND_WR_ONLY = 0101; /* See /usr/include/bits/fcntl.h */
|
|
fd = vg_do_syscall3(__NR_open, (UInt)pathname, O_CR_AND_WR_ONLY, 0);
|
|
/* VG_(printf)("result = %d\n", fd); */
|
|
if (VG_(is_kerror)(fd)) {
|
|
fd = -1;
|
|
} else {
|
|
VG_(chmod_u_rw)(fd);
|
|
if (VG_(is_kerror)(fd)) {
|
|
fd = -1;
|
|
}
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/* Returns -1 on failure. */
|
|
Int VG_(open_write) ( Char* pathname )
|
|
{
|
|
Int fd;
|
|
|
|
const int O_WRONLY_AND_TRUNC = 01001; /* See /usr/include/bits/fcntl.h */
|
|
fd = vg_do_syscall3(__NR_open, (UInt)pathname, O_WRONLY_AND_TRUNC, 0);
|
|
/* VG_(printf)("result = %d\n", fd); */
|
|
if (VG_(is_kerror)(fd)) {
|
|
fd = -1;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
void VG_(close) ( Int fd )
|
|
{
|
|
vg_do_syscall1(__NR_close, fd);
|
|
}
|
|
|
|
|
|
Int VG_(read) ( Int fd, void* buf, Int count)
|
|
{
|
|
Int res;
|
|
/* res = read( fd, buf, count ); */
|
|
res = vg_do_syscall3(__NR_read, fd, (UInt)buf, count);
|
|
if (VG_(is_kerror)(res)) res = -1;
|
|
return res;
|
|
}
|
|
|
|
Int VG_(write) ( Int fd, void* buf, Int count)
|
|
{
|
|
Int res;
|
|
/* res = write( fd, buf, count ); */
|
|
res = vg_do_syscall3(__NR_write, fd, (UInt)buf, count);
|
|
if (VG_(is_kerror)(res)) res = -1;
|
|
return res;
|
|
}
|
|
|
|
Int VG_(stat) ( Char* file_name, struct vki_stat* buf )
|
|
{
|
|
Int res;
|
|
res = vg_do_syscall2(__NR_stat, (UInt)file_name, (UInt)buf);
|
|
return
|
|
VG_(is_kerror)(res) ? (-1) : 0;
|
|
}
|
|
|
|
/* Misc functions looking for a proper home. */
|
|
|
|
/* We do getenv without libc's help by snooping around in
|
|
VG_(client_env) as determined at startup time. */
|
|
Char* VG_(getenv) ( Char* varname )
|
|
{
|
|
Int i, n;
|
|
n = VG_(strlen)(varname);
|
|
for (i = 0; VG_(client_envp)[i] != NULL; i++) {
|
|
Char* s = VG_(client_envp)[i];
|
|
if (VG_(strncmp)(varname, s, n) == 0 && s[n] == '=') {
|
|
return & s[n+1];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* You'd be amazed how many places need to know the current pid. */
|
|
Int VG_(getpid) ( void )
|
|
{
|
|
Int res;
|
|
/* res = getpid(); */
|
|
res = vg_do_syscall0(__NR_getpid);
|
|
return res;
|
|
}
|
|
|
|
/* Return -1 if error, else 0. NOTE does not indicate return code of
|
|
child! */
|
|
Int VG_(system) ( Char* cmd )
|
|
{
|
|
Int pid, res;
|
|
void* environ[1] = { NULL };
|
|
if (cmd == NULL)
|
|
return 1;
|
|
pid = vg_do_syscall0(__NR_fork);
|
|
if (VG_(is_kerror)(pid))
|
|
return -1;
|
|
if (pid == 0) {
|
|
/* child */
|
|
Char* argv[4];
|
|
argv[0] = "/bin/sh";
|
|
argv[1] = "-c";
|
|
argv[2] = cmd;
|
|
argv[3] = 0;
|
|
(void)vg_do_syscall3(__NR_execve,
|
|
(UInt)"/bin/sh", (UInt)argv, (UInt)&environ);
|
|
/* If we're still alive here, execve failed. */
|
|
return -1;
|
|
} else {
|
|
/* parent */
|
|
res = vg_do_syscall3(__NR_waitpid, pid, (UInt)NULL, 0);
|
|
if (VG_(is_kerror)(res)) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Support for a millisecond-granularity counter using RDTSC.
|
|
------------------------------------------------------------------ */
|
|
|
|
static __inline__ ULong do_rdtsc_insn ( void )
|
|
{
|
|
ULong x;
|
|
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
|
|
return x;
|
|
}
|
|
|
|
/* 0 = pre-calibration, 1 = calibration, 2 = running */
|
|
static Int rdtsc_calibration_state = 0;
|
|
static ULong rdtsc_ticks_per_millisecond = 0; /* invalid value */
|
|
|
|
static struct vki_timeval rdtsc_cal_start_timeval;
|
|
static struct vki_timeval rdtsc_cal_end_timeval;
|
|
|
|
static ULong rdtsc_cal_start_raw;
|
|
static ULong rdtsc_cal_end_raw;
|
|
|
|
UInt VG_(read_millisecond_timer) ( void )
|
|
{
|
|
ULong rdtsc_now;
|
|
vg_assert(rdtsc_calibration_state == 2);
|
|
rdtsc_now = do_rdtsc_insn();
|
|
vg_assert(rdtsc_now > rdtsc_cal_end_raw);
|
|
rdtsc_now -= rdtsc_cal_end_raw;
|
|
rdtsc_now /= rdtsc_ticks_per_millisecond;
|
|
return (UInt)rdtsc_now;
|
|
}
|
|
|
|
|
|
void VG_(start_rdtsc_calibration) ( void )
|
|
{
|
|
Int res;
|
|
vg_assert(rdtsc_calibration_state == 0);
|
|
rdtsc_calibration_state = 1;
|
|
rdtsc_cal_start_raw = do_rdtsc_insn();
|
|
res = vg_do_syscall2(__NR_gettimeofday, (UInt)&rdtsc_cal_start_timeval,
|
|
(UInt)NULL);
|
|
vg_assert(!VG_(is_kerror)(res));
|
|
}
|
|
|
|
void VG_(end_rdtsc_calibration) ( void )
|
|
{
|
|
Int res, loops;
|
|
ULong cpu_clock_MHZ;
|
|
ULong cal_clock_ticks;
|
|
ULong cal_wallclock_microseconds;
|
|
ULong wallclock_start_microseconds;
|
|
ULong wallclock_end_microseconds;
|
|
struct vki_timespec req;
|
|
struct vki_timespec rem;
|
|
|
|
vg_assert(rdtsc_calibration_state == 1);
|
|
rdtsc_calibration_state = 2;
|
|
|
|
/* Try and delay for 20 milliseconds, so that we can at least have
|
|
some minimum level of accuracy. */
|
|
req.tv_sec = 0;
|
|
req.tv_nsec = 20 * 1000 * 1000;
|
|
loops = 0;
|
|
while (True) {
|
|
res = VG_(nanosleep)(&req, &rem);
|
|
vg_assert(res == 0 /*ok*/ || res == 1 /*interrupted*/);
|
|
if (res == 0)
|
|
break;
|
|
if (rem.tv_sec == 0 && rem.tv_nsec == 0)
|
|
break;
|
|
req = rem;
|
|
loops++;
|
|
if (loops > 100)
|
|
VG_(panic)("calibration nanosleep loop failed?!");
|
|
}
|
|
|
|
/* Now read both timers, and do the Math. */
|
|
rdtsc_cal_end_raw = do_rdtsc_insn();
|
|
res = vg_do_syscall2(__NR_gettimeofday, (UInt)&rdtsc_cal_end_timeval,
|
|
(UInt)NULL);
|
|
|
|
vg_assert(rdtsc_cal_end_raw > rdtsc_cal_start_raw);
|
|
cal_clock_ticks = rdtsc_cal_end_raw - rdtsc_cal_start_raw;
|
|
|
|
wallclock_start_microseconds
|
|
= (1000000ULL * (ULong)(rdtsc_cal_start_timeval.tv_sec))
|
|
+ (ULong)(rdtsc_cal_start_timeval.tv_usec);
|
|
wallclock_end_microseconds
|
|
= (1000000ULL * (ULong)(rdtsc_cal_end_timeval.tv_sec))
|
|
+ (ULong)(rdtsc_cal_end_timeval.tv_usec);
|
|
vg_assert(wallclock_end_microseconds > wallclock_start_microseconds);
|
|
cal_wallclock_microseconds
|
|
= wallclock_end_microseconds - wallclock_start_microseconds;
|
|
|
|
/* Since we just nanoslept for 20 ms ... */
|
|
vg_assert(cal_wallclock_microseconds >= 20000);
|
|
|
|
/* Now we know (roughly) that cal_clock_ticks on RDTSC take
|
|
cal_wallclock_microseconds elapsed time. Calculate the RDTSC
|
|
ticks-per-millisecond value. */
|
|
if (0)
|
|
VG_(printf)("%lld ticks in %lld microseconds\n",
|
|
cal_clock_ticks, cal_wallclock_microseconds );
|
|
|
|
rdtsc_ticks_per_millisecond
|
|
= cal_clock_ticks / (cal_wallclock_microseconds / 1000ULL);
|
|
cpu_clock_MHZ
|
|
= (1000ULL * rdtsc_ticks_per_millisecond) / 1000000ULL;
|
|
if (VG_(clo_verbosity) >= 1)
|
|
VG_(message)(Vg_UserMsg, "Estimated CPU clock rate is %d MHz",
|
|
(UInt)cpu_clock_MHZ);
|
|
if (cpu_clock_MHZ < 100 || cpu_clock_MHZ > 10000)
|
|
VG_(panic)("end_rdtsc_calibration: "
|
|
"estimated CPU MHz outside range 100 .. 10000");
|
|
/* Paranoia about division by zero later. */
|
|
vg_assert(rdtsc_ticks_per_millisecond != 0);
|
|
if (0)
|
|
VG_(printf)("ticks per millisecond %llu\n",
|
|
rdtsc_ticks_per_millisecond);
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Primitive support for bagging memory via mmap.
|
|
------------------------------------------------------------------ */
|
|
|
|
void* VG_(get_memory_from_mmap) ( Int nBytes )
|
|
{
|
|
static UInt tot_alloc = 0;
|
|
void* p = VG_(mmap)( 0, nBytes,
|
|
VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC,
|
|
VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, -1, 0 );
|
|
if (p != ((void*)(-1))) {
|
|
tot_alloc += (UInt)nBytes;
|
|
if (0)
|
|
VG_(printf)("get_memory_from_mmap: %d tot, %d req\n",
|
|
tot_alloc, nBytes);
|
|
return p;
|
|
}
|
|
VG_(printf)("vg_get_memory_from_mmap failed on request of %d\n",
|
|
nBytes);
|
|
VG_(panic)("vg_get_memory_from_mmap: out of memory! Fatal! Bye!\n");
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end vg_mylibc.c ---*/
|
|
/*--------------------------------------------------------------------*/
|