#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "archtrace.h"
#pragma profile 0
/*
* this is the wrong place for this, and a very large hammer,
* but at least we are controlling access to kernel memory.
* must match mmuinit()
*/
static void
kmem(int write)
{
ulong x, *p;
for(x = KTZERO; x < (ulong)etext; x += BY2PG){
p = mmuwalk(m->pdb, x, 2, 0);
if(p == nil)
panic("kmem");
if(write)
*p |= PTEWRITE;
else
*p &= ~PTEWRITE;
}
}
enum {
CALL = 0xe8,
JMP = 0xeb,
RET = 0xc3,
NOP = 0x90,
};
char*
archtracectlr(Archtrace *a, char *p, char *e)
{
int i;
for(i = 0; i < a->nprobe; i++)
p = seprint(p, e, "# tracept %c %#.8p\n",
a->probe[i]==NOP? 'X': 'E', a->text[i]);
return p;
}
void
archtraceinstall(Archtrace *a)
{
int i;
kmem(1);
for(i = 0; i < a->nprobe; i++)
*a->text[i] = a->probe[i];
kmem(0);
}
void
archtraceuninstall(Archtrace *a)
{
int i;
kmem(1);
for(i = 0; i < a->nprobe; i++)
*a->text[i] = a->orig[i];
kmem(0);
}
static int
call4(uchar *call, uchar *targ)
{
uchar buf[5];
ulong l;
buf[0] = CALL;
l = targ - (call + 5);
buf[1] = l;
buf[2] = l>>8;
buf[3] = l>>16;
buf[4] = l>>24;
return memcmp(buf, call, 5);
}
static uchar startsig[] = {JMP, 0x05};
int
mkarchtrace(Archtrace *a, uchar *start, uchar **endp)
{
uchar *p, *end;
/*
* search by signatures. we don't really
* care we get a whole function or not.
*/
end = *endp;
for(p = start; p + 6 <= end; p++)
if(memcmp(p, startsig, sizeof startsig) == 0){
if(call4(p + 2, (uchar*)_tracein) == 0){
a->text[a->nprobe] = start + 1;
a->orig[a->nprobe] = *a->text[a->nprobe];
a->probe[a->nprobe] = 0; /* jmp 2f */
a->nprobe++;
}
}else if(p[0] == RET && p[6] == RET){
if(call4(p + 1, (uchar*)_traceout) == 0){
if(a->nprobe == Maxprobe)
error("too many exits");
a->text[a->nprobe] = p;
a->orig[a->nprobe] = *a->text[a->nprobe];
a->probe[a->nprobe] = NOP;
a->nprobe++;
}
}
// *endp = a->text[a->nprobe - 1];
return 0;
}
|