#include <sys/param.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <machine/ptrace.h>
#include <machine/reg.h>
#include <signal.h>
#include <wchar.h>
#include <string.h>
#include <libdisasm/libdis.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LINE_SIZE 70
#define OPCODES 15
#define CSTACKSIZE 200

/*

0x2804d418 <_rtld_bind_start>:  pushf
0x2804d419 <_rtld_bind_start+1>:        push   %eax
0x2804d41a <_rtld_bind_start+2>:        push   %edx
0x2804d41b <_rtld_bind_start+3>:        push   %ecx
0x2804d41c <_rtld_bind_start+4>:        pushl  0x14(%esp)
0x2804d420 <_rtld_bind_start+8>:        pushl  0x14(%esp)
0x2804d424 <_rtld_bind_start+12>:       call   0x2804e1a0 <_rtld_bind>
0x2804d429 <_rtld_bind_start+17>:       add    $0x8,%esp
0x2804d42c <_rtld_bind_start+20>:       mov    %eax,0x14(%esp)
0x2804d430 <_rtld_bind_start+24>:       pop    %ecx
0x2804d431 <_rtld_bind_start+25>:       pop    %edx
0x2804d432 <_rtld_bind_start+26>:       pop    %eax
0x2804d433 <_rtld_bind_start+27>:       popf
0x2804d434 <_rtld_bind_start+28>:       lea    0x4(%esp),%esp
0x2804d438 <_rtld_bind_start+32>:       ret


0x2804d418 <_rtld_bind_start>:  0x9c    0x50    0x52    0x51    0xff    0x74    0x24    0x14
0x2804d420 <_rtld_bind_start+8>:        0xff    0x74    0x24    0x14    0xe8    0x77    0x0d    0x00
0x2804d428 <_rtld_bind_start+16>:       0x00    0x83    0xc4    0x08    0x89    0x44    0x24    0x14
0x2804d430 <_rtld_bind_start+24>:       0x59    0x5a    0x58    0x9d    0x8d    0x64    0x24    0x04
0x2804d438 <_rtld_bind_start+32>:       0xc3

*/
     extern char **environ;




int main (int argc, char **argv){

  int child, status,i, opsize,cstackptr;
  struct ptrace_bss_addrs lastbranch, lastexception; 
  struct reg registers;
  struct ptrace_io_desc io_desc;
  unsigned char buf[OPCODES];
  struct ptrace_bss_addrs callstack[CSTACKSIZE];

  x86_insn_t insn;         /* instruction */
  x86_init( opt_none, NULL);
  cstackptr = 0;

  child = fork();
  if( child!=0) {
     wait(&status);
     ptrace( PT_BSSTEP, child, 0, 1); 
     while(ptrace( PT_STEP, child, (caddr_t)1, 0)==0) {
        wait( &status);
	if(WIFSTOPPED(status)) printf("received signal %u\n", WSTOPSIG(status));
	if((i=ptrace( PT_GETREGS, child, &registers,0)) == 0) /*printf("EIP: 0x%08x \n", registers.r_eip)*/;
        if(ptrace( PT_LASTBRANCH, child, &lastbranch, 0)==0);
	if(ptrace( PT_LASTEXCEPTION, child, &lastexception, 0)==0);
        io_desc.piod_op = PIOD_READ_I;
        io_desc.piod_offs = lastbranch.from;
        io_desc.piod_addr = &buf;
        io_desc.piod_len = 10;
	if( (lastbranch.from < 0xc0000000) &&  (ptrace( PT_IO, child, &io_desc, 0) == 0)) {
           opsize = x86_disasm(buf, OPCODES, lastbranch.from, 0, &insn);
           switch( insn.type) {
              case insn_jmp:
                 printf( " JMP from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 break;
              case insn_jcc:
                 printf( " JCC from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 break;
              case insn_int:
                 printf( " INT from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 break;
              case insn_iret:
                 printf( "IRET from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 break;

              case insn_return:
                 printf( " RET from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x - ", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip, registers.r_eip);
                 if( cstackptr > 0 && abs( callstack[cstackptr-1].from - lastbranch.to) < 7) {
                    cstackptr--;
                    printf( "    matching last CALL from 0x%08x to 0x%08x\n", callstack[cstackptr].from, callstack[cstackptr].to);
                 } else {
                    printf( "not matching last CALL from 0x%08x to 0x%08x\n", callstack[cstackptr-1].from, callstack[cstackptr-1].to);
                 }
                 break;
              case insn_call:
              case insn_callcc:
                 printf( "CALL from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 if( cstackptr == CSTACKSIZE) {
                    printf( "Call stack size limit hit\n");
                 } else {
                    callstack[cstackptr++] = lastbranch;
                 }
                 break;
              default:
                 printf("Some ODD Instruction I didn't catch... sucks!\n");
                 break;
           }
        } else if(lastbranch.from > 0xc0000000 && lastexception.from < 0xc0000000) { 
           io_desc.piod_op = PIOD_READ_I;
           io_desc.piod_offs = lastexception.from;
           io_desc.piod_addr = &buf;
           io_desc.piod_len = 10;
           if( ptrace( PT_IO, child, &io_desc, 0) == 0) {
           opsize = x86_disasm(buf, OPCODES, lastbranch.from, 0, &insn);
           switch( insn.type) {
              case insn_int:
                 printf( " INT from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 break;
              case insn_iret:
                 printf( "IRET from 0x%08x to 0x%08x, last exception from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastbranch.from, lastbranch.to, lastexception.from, lastexception.to, registers.r_eip);
                 break;
              default:
                 printf( "SOME other instruction\n");
                 break;
           }
        }

/*           printf("Oddity: last exception from 0x%08x to 0x%08x, last branch from 0x%08x to 0x%08x, EIP is 0x%08x\n", lastexception.from, lastexception.to, lastbranch.from, lastbranch.to, registers.r_eip); */
        }
     }
     printf( "Process %u, '%s' terminated, ptrace returned: %i\n", child, argv[1], i);
     x86_cleanup();
  } else { 
     ptrace(PT_TRACE_ME, child, 0, 0);  
     execve( argv[1], &argv[1], environ);
  } 
 exit(0); 
}
