mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 02:18:37 +00:00
412 lines
10 KiB
C
412 lines
10 KiB
C
|
|
/* This is a replacement for the standard libpthread.so. It is loaded
|
|
as part of the client's image (if required) and directs pthread
|
|
calls through to Valgrind's request mechanism.
|
|
|
|
A couple of caveats.
|
|
|
|
1. Since it's a binary-compatible replacement for an existing library,
|
|
we must take care to used exactly the same data layouts, etc, as
|
|
the standard pthread.so does.
|
|
|
|
2. Since this runs as part of the client, there are no specific
|
|
restrictions on what headers etc we can include, so long as
|
|
this libpthread.so does not end up having dependencies on .so's
|
|
which the real one doesn't.
|
|
|
|
Later ... it appears we cannot call file-related stuff in libc here,
|
|
perhaps fair enough. Be careful what you call from here. Even exit()
|
|
doesn't work (gives infinite recursion and then stack overflow); hence
|
|
myexit(). Also fprintf doesn't seem safe.
|
|
*/
|
|
|
|
#include "valgrind.h" /* For the request-passing mechanism */
|
|
#include "vg_include.h" /* For the VG_USERREQ__* constants */
|
|
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Helpers. We have to be pretty self-sufficient.
|
|
------------------------------------------------------------------ */
|
|
|
|
static
|
|
void myexit ( int arg )
|
|
{
|
|
int __res;
|
|
__asm__ volatile ("movl %%ecx, %%ebx ; int $0x80"
|
|
: "=a" (__res)
|
|
: "0" (__NR_exit),
|
|
"c" (arg) );
|
|
/* We don't bother to mention the fact that this asm trashes %ebx,
|
|
since it won't return. If you ever do let it return ... fix
|
|
this! */
|
|
}
|
|
|
|
|
|
/* Give up without using printf etc, since they seem to give
|
|
segfaults. */
|
|
static
|
|
void ensure_valgrind ( char* caller )
|
|
{
|
|
char* str;
|
|
int is_valgrind = RUNNING_ON_VALGRIND;
|
|
if (!is_valgrind) {
|
|
str = "\nvalgrind-ed process: vg_libpthread.so: "
|
|
"pthread call when\n";
|
|
write(2, str, strlen(str));
|
|
str = "not running on valgrind; aborting! "
|
|
"This is probably a bug in\n";
|
|
write(2, str, strlen(str));
|
|
str = "valgrind. Please report it to me at: "
|
|
"jseward@acm.org. Thanks.\n";
|
|
write(2, str, strlen(str));
|
|
str = "unexpectedly called function is: ";
|
|
write(2, str, strlen(str));
|
|
write(2, caller, strlen(caller));
|
|
str = "\n\n";
|
|
write(2, str, strlen(str));
|
|
myexit(1);
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
void barf ( char* str )
|
|
{
|
|
char buf[100];
|
|
buf[0] = 0;
|
|
strcat(buf, "\nvg_libpthread.so: ");
|
|
strcat(buf, str);
|
|
strcat(buf, "\n\n");
|
|
write(2, buf, strlen(buf));
|
|
myexit(1);
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
Pass pthread_ calls to Valgrind's request mechanism.
|
|
------------------------------------------------------------------ */
|
|
|
|
int
|
|
pthread_create (pthread_t *__restrict __thread,
|
|
__const pthread_attr_t *__restrict __attr,
|
|
void *(*__start_routine) (void *),
|
|
void *__restrict __arg)
|
|
{
|
|
int res;
|
|
ensure_valgrind("pthread_create");
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_CREATE,
|
|
__thread, __attr, __start_routine, __arg);
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
pthread_join (pthread_t __th, void **__thread_return)
|
|
{
|
|
int res;
|
|
ensure_valgrind("pthread_join");
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_JOIN,
|
|
__th, __thread_return, 0, 0);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* What are these? Anybody know? I don't. */
|
|
|
|
void _pthread_cleanup_push_defer ( void )
|
|
{
|
|
// char* str = "_pthread_cleanup_push_defer\n";
|
|
// write(2, str, strlen(str));
|
|
}
|
|
|
|
void _pthread_cleanup_pop_restore ( void )
|
|
{
|
|
// char* str = "_pthread_cleanup_pop_restore\n";
|
|
// write(2, str, strlen(str));
|
|
}
|
|
|
|
|
|
static int thread_specific_errno[VG_N_THREADS];
|
|
|
|
int* __errno_location ( void )
|
|
{
|
|
int tid;
|
|
ensure_valgrind("__errno_location");
|
|
VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_GET_THREADID,
|
|
0, 0, 0, 0);
|
|
/* 'cos I'm paranoid ... */
|
|
if (tid < 0 || tid >= VG_N_THREADS)
|
|
barf("__errno_location: invalid ThreadId");
|
|
return & thread_specific_errno[tid];
|
|
}
|
|
|
|
|
|
int pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
|
{
|
|
char* str = "pthread_mutexattr_init\n";
|
|
write(2, str, strlen(str));
|
|
return 0;
|
|
}
|
|
|
|
int pthread_mutex_init(pthread_mutex_t *mutex,
|
|
const pthread_mutexattr_t *mutexattr)
|
|
{
|
|
int res;
|
|
char* str = "pthread_mutex_init\n";
|
|
write(2, str, strlen(str));
|
|
ensure_valgrind("pthread_mutex_init");
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_MUTEX_INIT,
|
|
mutex, mutexattr, 0, 0);
|
|
return res;
|
|
}
|
|
|
|
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
|
|
{
|
|
char* str = "pthread_mutexattr_destroy\n";
|
|
write(2, str, strlen(str));
|
|
return 0;
|
|
}
|
|
|
|
int pthread_mutex_lock(pthread_mutex_t *mutex)
|
|
{
|
|
int res;
|
|
if (!(RUNNING_ON_VALGRIND)) {
|
|
char* str = "pthread_mutex_lock-NOT-INSIDE-VALGRIND\n";
|
|
write(2, str, strlen(str));
|
|
return 0;
|
|
} else {
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_MUTEX_LOCK,
|
|
mutex, 0, 0, 0);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
|
{
|
|
int res;
|
|
if (!(RUNNING_ON_VALGRIND)) {
|
|
char* str = "pthread_mutex_unlock-NOT-INSIDE-VALGRIND\n";
|
|
write(2, str, strlen(str));
|
|
return 0;
|
|
} else {
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_MUTEX_UNLOCK,
|
|
mutex, 0, 0, 0);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
pthread_t pthread_self(void)
|
|
{
|
|
int tid;
|
|
ensure_valgrind("pthread_self");
|
|
VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_GET_THREADID,
|
|
0, 0, 0, 0);
|
|
if (tid < 0 || tid >= VG_N_THREADS)
|
|
barf("pthread_self: invalid ThreadId");
|
|
return tid;
|
|
}
|
|
|
|
int pthread_mutex_destroy(pthread_mutex_t *mutex)
|
|
{
|
|
int res;
|
|
if (!(RUNNING_ON_VALGRIND)) {
|
|
char* str = "pthread_mutex_destroy-NOT-INSIDE-VALGRIND\n";
|
|
write(2, str, strlen(str));
|
|
return 0;
|
|
} else {
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_MUTEX_DESTROY,
|
|
mutex, 0, 0, 0);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
int pthread_setcanceltype(int type, int *oldtype)
|
|
{
|
|
char* str = "pthread_setcanceltype\n";
|
|
write(2, str, strlen(str));
|
|
return 0;
|
|
}
|
|
|
|
|
|
int pthread_cancel(pthread_t thread)
|
|
{
|
|
int res;
|
|
ensure_valgrind("pthread_cancel");
|
|
VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
|
|
VG_USERREQ__PTHREAD_CANCEL,
|
|
thread, 0, 0, 0);
|
|
return res;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------
|
|
These are here (I think) because they are deemed cancellation
|
|
points by POSIX. For the moment we'll simply pass the call along
|
|
to the corresponding thread-unaware (?) libc routine.
|
|
------------------------------------------------------------------ */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
extern
|
|
int __libc_sigaction
|
|
(int signum,
|
|
const struct sigaction *act,
|
|
struct sigaction *oldact);
|
|
int sigaction(int signum,
|
|
const struct sigaction *act,
|
|
struct sigaction *oldact)
|
|
{
|
|
// char* str = "sigaction\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_sigaction(signum, act, oldact);
|
|
}
|
|
|
|
|
|
extern
|
|
int __libc_connect(int sockfd,
|
|
const struct sockaddr *serv_addr,
|
|
socklen_t addrlen);
|
|
int connect(int sockfd,
|
|
const struct sockaddr *serv_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
// char* str = "connect\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_connect(sockfd, serv_addr, addrlen);
|
|
}
|
|
|
|
|
|
extern
|
|
int __libc_fcntl(int fd, int cmd, long arg);
|
|
int fcntl(int fd, int cmd, long arg)
|
|
{
|
|
// char* str = "fcntl\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_fcntl(fd, cmd, arg);
|
|
}
|
|
|
|
|
|
extern
|
|
ssize_t __libc_write(int fd, const void *buf, size_t count);
|
|
ssize_t write(int fd, const void *buf, size_t count)
|
|
{
|
|
// char* str = "write\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_write(fd, buf, count);
|
|
}
|
|
|
|
|
|
extern
|
|
ssize_t __libc_read(int fd, void *buf, size_t count);
|
|
ssize_t read(int fd, void *buf, size_t count)
|
|
{
|
|
// char* str = "read\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_read(fd, buf, count);
|
|
}
|
|
|
|
|
|
extern
|
|
int __libc_open(const char *pathname, int flags);
|
|
int open(const char *pathname, int flags)
|
|
{
|
|
// char* str = "open\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_open(pathname, flags);
|
|
}
|
|
|
|
|
|
extern
|
|
int __libc_close(int fd);
|
|
int close(int fd)
|
|
{
|
|
// char* str = "open\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_close(fd);
|
|
}
|
|
|
|
|
|
extern
|
|
int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
|
|
int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
|
|
{
|
|
// char* str = "accept\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_accept(s, addr, addrlen);
|
|
}
|
|
|
|
|
|
extern
|
|
pid_t __libc_fork(void);
|
|
pid_t fork(void)
|
|
{
|
|
// char* str = "fork\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_fork();
|
|
}
|
|
|
|
|
|
extern
|
|
pid_t __libc_waitpid(pid_t pid, int *status, int options);
|
|
pid_t waitpid(pid_t pid, int *status, int options)
|
|
{
|
|
// char* str = "waitpid\n";
|
|
// write(2, str, strlen(str));
|
|
return __libc_waitpid(pid, status, options);
|
|
}
|
|
|
|
|
|
extern
|
|
int __libc_nanosleep(const struct timespec *req, struct timespec *rem);
|
|
int nanosleep(const struct timespec *req, struct timespec *rem)
|
|
{
|
|
return __libc_nanosleep(req, rem);
|
|
}
|
|
|
|
extern
|
|
int __libc_fsync(int fd);
|
|
int fsync(int fd)
|
|
{
|
|
return __libc_fsync(fd);
|
|
}
|
|
|
|
/* I've no idea what these are, but they get called quite a lot.
|
|
Anybody know? */
|
|
|
|
#undef _IO_flockfile
|
|
void _IO_flockfile ( _IO_FILE * file )
|
|
{
|
|
// char* str = "_IO_flockfile\n";
|
|
// write(2, str, strlen(str));
|
|
// barf("_IO_flockfile");
|
|
}
|
|
|
|
#undef _IO_funlockfile
|
|
void _IO_funlockfile ( _IO_FILE * file )
|
|
{
|
|
// char* str = "_IO_funlockfile\n";
|
|
// write(2, str, strlen(str));
|
|
//barf("_IO_funlockfile");
|
|
}
|
|
|