Package vtrace :: Package platforms :: Module linux
[hide private]
[frames] | no frames]

Source Code for Module vtrace.platforms.linux

  1  """ 
  2  Linux Platform Module 
  3  """ 
  4  # Copyright (C) 2007 Invisigoth - See LICENSE file for details 
  5  import os 
  6  import time 
  7  import struct 
  8  import signal 
  9  import traceback 
 10  import platform 
 11   
 12  import envi.cli as e_cli 
 13  import envi.memory as e_mem 
 14  import envi.registers as e_reg 
 15   
 16  import vtrace 
 17   
 18  import vtrace.archs.arm as v_arm 
 19  import vtrace.archs.i386 as v_i386 
 20  import vtrace.archs.amd64 as v_amd64 
 21   
 22  import vtrace.platforms.base as v_base 
 23  import vtrace.platforms.posix as v_posix 
 24   
 25  from ctypes import * 
 26  import ctypes.util as cutil 
 27   
 28  if os.getenv('ANDROID_ROOT'): 
 29      libc = CDLL('/system/lib/libc.so') 
 30  else: 
 31      libc = CDLL(cutil.find_library("c")) 
 32   
 33  libc.lseek64.restype = c_ulonglong 
 34  libc.lseek64.argtypes = [c_uint, c_ulonglong, c_uint] 
 35  libc.read.restype = c_long 
 36  libc.read.argtypes = [c_uint, c_void_p, c_long] 
 37  libc.write.restype = c_long 
 38  libc.write.argtypes = [c_uint, c_void_p, c_long] 
 39   
 40  O_RDWR = 2 
 41  O_LARGEFILE = 0x8000 
 42   
 43  MAP_ANONYMOUS = 0x20 
 44  MAP_PRIVATE = 0x02 
 45   
 46  # Linux specific ptrace extensions 
 47  PT_GETREGS = 12 
 48  PT_SETREGS = 13 
 49  PT_GETFPREGS = 14 
 50  PT_SETFPREGS = 15 
 51  PT_ATTACH = 16 
 52  PT_DETACH = 17 
 53  PT_GETFPXREGS = 18 
 54  PT_SETFPXREGS = 19 
 55  PT_SYSCALL = 24 
 56  PT_SETOPTIONS = 0x4200 
 57  PT_GETEVENTMSG = 0x4201 
 58  PT_GETSIGINFO = 0x4202 
 59  PT_SETSIGINFO = 0x4203 
 60  # PT set options stuff.  ONLY TRACESYSGOOD may be used in 2.4... 
 61  PT_O_TRACESYSGOOD   = 0x00000001 # add 0x80 to TRAP when generated by syscall 
 62  # For each of the options below, the stop signal is (TRAP | PT_EVENT_FOO << 8) 
 63  PT_O_TRACEFORK      = 0x00000002 # Cause a trap at fork 
 64  PT_O_TRACEVFORK     = 0x00000004 # Cause a trap at vfork 
 65  PT_O_TRACECLONE     = 0x00000008 # Cause a trap at clone 
 66  PT_O_TRACEEXEC      = 0x00000010 # Cause a trap at exec 
 67  PT_O_TRACEVFORKDONE = 0x00000020 # Cause a trap when vfork done 
 68  PT_O_TRACEEXIT      = 0x00000040 # Cause a trap on exit 
 69  PT_O_MASK           = 0x0000007f 
 70  # Ptrace event types (TRAP | PT_EVENT_FOO << 8) means that type 
 71  # when using GETEVENTMSG for most of these, the new pid is the data 
 72  PT_EVENT_FORK       = 1 
 73  PT_EVENT_VFORK      = 2 
 74  PT_EVENT_CLONE      = 3 
 75  PT_EVENT_EXEC       = 4 
 76  PT_EVENT_VFORK_DONE = 5 
 77  PT_EVENT_EXIT       = 6 
 78   
 79  # Used to tell some of the additional events apart 
 80  SIG_LINUX_SYSCALL = signal.SIGTRAP | 0x80 
 81  SIG_LINUX_CLONE = signal.SIGTRAP | (PT_EVENT_CLONE << 8) 
 82  SIG_LINUX_EXIT = signal.SIGTRAP | (PT_EVENT_EXIT << 8) 
83 84 #following from Pandaboard ES (OMAP4460) Armv7a (cortex-a9) 85 -class user_regs_arm(Structure):
86 _fields_ = ( 87 ("r0", c_ulong), 88 ("r1", c_ulong), 89 ("r2", c_ulong), 90 ("r3", c_ulong), 91 ("r4", c_ulong), 92 ("r5", c_ulong), 93 ("r6", c_ulong), 94 ("r7", c_ulong), 95 ("r8", c_ulong), 96 ("r9", c_ulong), 97 ("r10", c_ulong), #aka 'sl' ? 98 ("r11", c_ulong), 99 ("r12", c_ulong), 100 ("sp", c_ulong), 101 ("lr", c_ulong), 102 ("pc", c_ulong), 103 ("cpsr", c_ulong), 104 ("orig_r0", c_ulong), 105 )
106
107 -class fp_reg_arm(Structure):
108 _fields_ = ( 109 ("sign1", c_long, 1), 110 ("unused", c_long, 15), 111 ("sign2", c_long, 1), 112 ("exponent", c_long, 14), 113 ("j", c_long, 1), 114 ("mantissa1", c_long, 31), 115 ("mantissa0", c_long, 32), 116 )
117
118 -class user_fpregs_arm(Structure):
119 _fields_ = ( 120 ("fpregs", fp_reg_arm*8), 121 ("fpsr", c_ulong, 32), 122 ("fpcr", c_ulong, 32), 123 ("ftype", c_ubyte*8), 124 ("init_flag", c_ulong), 125 )
126
127 -class USER_arm(Structure):
128 _fields_ = ( 129 ("regs", user_regs_arm), 130 ("u_fpvalid", c_long), 131 ("u_tsize", c_ulong), 132 ("u_dsize", c_ulong), 133 ("u_ssize", c_ulong), 134 ("start_code", c_ulong), 135 ("start_stack",c_ulong), 136 ("signal", c_long), 137 ("reserved", c_long), 138 ("u_ar0", c_void_p), 139 ("magic", c_ulong), 140 ("u_comm", c_char*32), 141 ("u_debugreg", c_long*8), 142 ("fpregs", user_fpregs_arm), 143 ("u_fp0", c_void_p) 144 )
145
146 -class user_regs_i386(Structure):
147 _fields_ = ( 148 ("ebx", c_ulong), 149 ("ecx", c_ulong), 150 ("edx", c_ulong), 151 ("esi", c_ulong), 152 ("edi", c_ulong), 153 ("ebp", c_ulong), 154 ("eax", c_ulong), 155 ("ds", c_ushort), 156 ("__ds", c_ushort), 157 ("es", c_ushort), 158 ("__es", c_ushort), 159 ("fs", c_ushort), 160 ("__fs", c_ushort), 161 ("gs", c_ushort), 162 ("__gs", c_ushort), 163 ("orig_eax", c_ulong), 164 ("eip", c_ulong), 165 ("cs", c_ushort), 166 ("__cs", c_ushort), 167 ("eflags", c_ulong), 168 ("esp", c_ulong), 169 ("ss", c_ushort), 170 ("__ss", c_ushort), 171 )
172
173 174 -class USER_i386(Structure):
175 _fields_ = ( 176 # NOTE: Expand out the user regs struct so 177 # we can make one call to _rctx_Import 178 ("regs", user_regs_i386), 179 ("u_fpvalid", c_ulong), 180 ("u_tsize", c_ulong), 181 ("u_dsize", c_ulong), 182 ("u_ssize", c_ulong), 183 ("start_code", c_ulong), 184 ("start_stack",c_ulong), 185 ("signal", c_ulong), 186 ("reserved", c_ulong), 187 ("u_ar0", c_void_p), 188 ("u_fpstate", c_void_p), 189 ("magic", c_ulong), 190 ("u_comm", c_char*32), 191 ("debug0", c_ulong), 192 ("debug1", c_ulong), 193 ("debug2", c_ulong), 194 ("debug3", c_ulong), 195 ("debug4", c_ulong), 196 ("debug5", c_ulong), 197 ("debug6", c_ulong), 198 ("debug7", c_ulong), 199 )
200
201 -class user_regs_amd64(Structure):
202 _fields_ = [ 203 ('r15', c_uint64), 204 ('r14', c_uint64), 205 ('r13', c_uint64), 206 ('r12', c_uint64), 207 ('rbp', c_uint64), 208 ('rbx', c_uint64), 209 ('r11', c_uint64), 210 ('r10', c_uint64), 211 ('r9', c_uint64), 212 ('r8', c_uint64), 213 ('rax', c_uint64), 214 ('rcx', c_uint64), 215 ('rdx', c_uint64), 216 ('rsi', c_uint64), 217 ('rdi', c_uint64), 218 ('orig_rax', c_uint64), 219 ('rip', c_uint64), 220 ('cs', c_uint64), 221 ('eflags', c_uint64), 222 ('rsp', c_uint64), 223 ('ss', c_uint64), 224 ('fs_base', c_uint64), 225 ('gs_base', c_uint64), 226 ('ds', c_uint64), 227 ('es', c_uint64), 228 ('fs', c_uint64), 229 ('gs', c_uint64), 230 ]
231 232 intel_dbgregs = (0,1,2,3,6,7)
233 234 -class LinuxMixin(v_posix.PtraceMixin, v_posix.PosixMixin):
235 """ 236 The mixin to take care of linux specific platform traits. 237 (mostly proc) 238 """ 239
240 - def __init__(self):
241 # Wrap reads from proc in our worker thread 242 v_posix.PtraceMixin.__init__(self) 243 v_posix.PosixMixin.__init__(self) 244 self.memfd = None 245 self._stopped_cache = {} 246 self._stopped_hack = False 247 248 self.fireTracerThread() 249 250 self.initMode("Syscall", False, "Break On Syscalls")
251
252 - def setupMemFile(self, offset):
253 """ 254 A utility to open (if necissary) and seek the memfile 255 """ 256 if self.memfd == None: 257 self.memfd = libc.open("/proc/%d/mem" % self.pid, O_RDWR | O_LARGEFILE, 0755) 258 259 x = libc.lseek64(self.memfd, offset, 0)
260 261 @v_base.threadwrap
262 - def platformReadMemory(self, address, size):
263 """ 264 A *much* faster way of reading memory that the 4 bytes 265 per syscall allowed by ptrace 266 """ 267 self.setupMemFile(address) 268 # Use ctypes cause python implementation is teh ghey 269 buf = create_string_buffer(size) 270 x = libc.read(self.memfd, addressof(buf), size) 271 if x != size: 272 #libc.perror('libc.read %d (size: %d)' % (x,size)) 273 raise Exception("reading from invalid memory %s (%d returned)" % (hex(address), x)) 274 # We have to slice cause ctypes "helps" us by adding a null byte... 275 return buf.raw
276 277 @v_base.threadwrap
278 - def whynot_platformWriteMemory(self, address, data):
279 """ 280 A *much* faster way of writting memory that the 4 bytes 281 per syscall allowed by ptrace 282 """ 283 self.setupMemFile(address) 284 buf = create_string_buffer(data) 285 size = len(data) 286 x = libc.write(self.memfd, addressof(buf), size) 287 if x != size: 288 libc.perror('write mem failed: 0x%.8x (%d)' % (address, size)) 289 raise Exception("write memory failed: %d" % x) 290 return x
291
292 - def _findExe(self, pid):
293 exe = os.readlink("/proc/%d/exe" % pid) 294 if "(deleted)" in exe: 295 if "#prelink#" in exe: 296 exe = exe.split(".#prelink#")[0] 297 elif ";" in exe: 298 exe = exe.split(";")[0] 299 else: 300 exe = exe.split("(deleted)")[0].strip() 301 return exe
302 303 @v_base.threadwrap
304 - def platformExec(self, cmdline):
305 # Very similar to posix, but not 306 # quite close enough... 307 self.execing = True 308 cmdlist = e_cli.splitargs(cmdline) 309 os.stat(cmdlist[0]) 310 pid = os.fork() 311 312 if pid == 0: 313 v_posix.ptrace(v_posix.PT_TRACE_ME, 0, 0, 0) 314 # Make sure our parent gets some cycles 315 time.sleep(0.1) 316 os.execv(cmdlist[0], cmdlist) 317 sys.exit(-1) 318 319 if v_posix.ptrace(PT_ATTACH, pid, 0, 0) != 0: 320 raise Exception("PT_ATTACH failed! linux platformExec") 321 322 self.pthreads = [pid,] 323 self.setMeta("ExeName", self._findExe(pid)) 324 return pid
325 326 @v_base.threadwrap
327 - def platformAttach(self, pid):
328 self.pthreads = [pid,] 329 self.setMeta("ThreadId", pid) 330 if v_posix.ptrace(PT_ATTACH, pid, 0, 0) != 0: 331 raise Exception("PT_ATTACH failed!") 332 self.setMeta("ExeName", self._findExe(pid))
333
334 - def platformPs(self):
335 pslist = [] 336 for dname in os.listdir("/proc/"): 337 try: 338 if not dname.isdigit(): 339 continue 340 cmdline = file("/proc/%s/cmdline" % dname).read() 341 cmdline = cmdline.replace("\x00"," ") 342 if len(cmdline) > 0: 343 pslist.append((int(dname),cmdline)) 344 except: 345 pass # Permissions... quick process... whatev. 346 return pslist
347
348 - def _simpleCreateThreads(self):
349 for tid in self.threadsForPid( self.pid ): 350 if tid == self.pid: 351 continue 352 self.attachThread( tid )
353
354 - def attachThread(self, tid, attached=False):
355 self.doAttachThread(tid,attached=attached) 356 self.setMeta("ThreadId", tid) 357 self.fireNotifiers(vtrace.NOTIFY_CREATE_THREAD)
358 359 @v_base.threadwrap
360 - def detachThread(self, tid, ecode):
361 362 self.setMeta('ThreadId', tid) 363 self._fireExitThread(tid, ecode) 364 365 if v_posix.ptrace(PT_DETACH, tid, 0, 0) != 0: 366 raise Exception("ERROR ptrace detach failed for thread %d" % tid) 367 368 self.pthreads.remove(tid)
369 370 @v_base.threadwrap
371 - def platformWait(self):
372 # Blocking wait once... 373 pid, status = os.waitpid(-1, 0x40000002) 374 self.setMeta("ThreadId", pid) 375 # Stop the rest of the threads... 376 # why is linux debugging so Ghetto?!?! 377 if not self.stepping: # If we're stepping, only do the one 378 for tid in self.pthreads: 379 if tid == pid: 380 continue 381 try: 382 # We use SIGSTOP here because they can't mask it. 383 os.kill(tid, signal.SIGSTOP) 384 os.waitpid(tid, 0x40000002) 385 except Exception, e: 386 print "WARNING TID is invalid %d %s" % (tid,e) 387 return pid,status
388 389 @v_base.threadwrap
390 - def platformContinue(self):
391 cmd = v_posix.PT_CONTINUE 392 if self.getMode("Syscall", False): 393 cmd = PT_SYSCALL 394 pid = self.getPid() 395 sig = self.getCurrentSignal() 396 if sig == None: 397 sig = 0 398 # Only deliver signals to the main thread 399 if v_posix.ptrace(cmd, pid, 0, sig) != 0: 400 libc.perror('ptrace PT_CONTINUE failed for pid %d' % pid) 401 raise Exception("ERROR ptrace failed for pid %d" % pid) 402 403 for tid in self.pthreads: 404 if tid == pid: 405 continue 406 if v_posix.ptrace(cmd, tid, 0, 0) != 0: 407 pass
408 409 @v_base.threadwrap
410 - def platformStepi(self):
411 self.stepping = True 412 tid = self.getMeta("ThreadId", 0) 413 if v_posix.ptrace(v_posix.PT_STEP, tid, 0, 0) != 0: 414 raise Exception("ERROR ptrace failed!")
415 416 @v_base.threadwrap
417 - def platformDetach(self):
418 libc.close(self.memfd) 419 for tid in self.pthreads: 420 v_posix.ptrace(PT_DETACH, tid, 0, 0)
421 422 @v_base.threadwrap
423 - def doAttachThread(self, tid, attached=False):
424 """ 425 Do the work for attaching a thread. This must be *under* 426 attachThread() so callers in notifiers may call it (because 427 it's also gotta be thread wrapped). 428 """ 429 if not attached: 430 if v_posix.ptrace(PT_ATTACH, tid, 0, 0) != 0: 431 raise Exception("ERROR ptrace attach failed for thread %d" % tid) 432 433 # We may have already revcieved the stop signal 434 if not self._stopped_cache.pop( tid, None ): 435 os.waitpid(tid, 0x40000002) 436 437 self.setupPtraceOptions(tid) 438 self.pthreads.append(tid)
439 440 @v_base.threadwrap
441 - def setupPtraceOptions(self, tid):
442 """ 443 Called per pid/tid to setup proper options 444 for ptrace. 445 """ 446 opts = PT_O_TRACESYSGOOD 447 if platform.release()[:3] in ('2.6','3.0','3.1','3.2'): 448 opts |= PT_O_TRACECLONE | PT_O_TRACEEXIT 449 x = v_posix.ptrace(PT_SETOPTIONS, tid, 0, opts) 450 if x != 0: 451 libc.perror('ptrace PT_SETOPTION failed for thread %d' % tid)
452
453 - def threadsForPid(self, pid):
454 ret = [] 455 tpath = "/proc/%s/task" % pid 456 if os.path.exists(tpath): 457 for pidstr in os.listdir(tpath): 458 ret.append(int(pidstr)) 459 return ret
460
461 - def platformProcessEvent(self, event):
462 # Skim some linux specific events before passing to posix 463 tid, status = event 464 if os.WIFSTOPPED(status): 465 sig = status >> 8 # Cant use os.WSTOPSIG() here... 466 #print('STOPPED: %d %d %.8x %d' % (self.pid, tid, status, sig)) 467 468 # Ok... this is a crazy little state engine that tries 469 # to account for the discrepancies in how linux posts 470 # signals to the debugger... 471 472 # Thread Creation: 473 # In each case below, the kernel may deliver 474 # any of the 3 signals in any order... ALSO 475 # (and more importantly) *if* the kernel sends 476 # SIGSTOP to the thread first, the debugger 477 # will get a SIGSTOP *instead* of SIG_LINUX_CLONE 478 # ( this will go back and forth on *ONE BOX* with 479 # the same kernel version... Finally squished it 480 # because it presents more frequently ( 1 in 10 ) 481 # on my new ARM linux dev board. WTF?!1?!one?!? ) 482 # 483 # Case 1 (SIG_LINUX_CLONE): 484 # debugger gets SIG_LINUX CLONE as expected 485 # and can then use ptrace(PT_GETEVENTMSG) 486 # to get new TID and attach as normal 487 # Case 2 (SIGSTOP delivered to thread) 488 # Thread is already stoped and attached but 489 # parent debugger doesn't know yet. We add 490 # the tid to the stopped_cache so when the 491 # kernel gets around to telling the debugger 492 # we don't wait on him again. 493 # Case 3 (SIGSTOP delivered to debugger) 494 # In both case 2 and case 3, this will cause 495 # the SIG_LINUX_CLONE to be skipped. Either 496 # way, we should head down into thread attach. 497 # ( The thread may be already stopped ) 498 if sig == SIG_LINUX_SYSCALL: 499 self.fireNotifiers(vtrace.NOTIFY_SYSCALL) 500 501 elif sig == SIG_LINUX_EXIT: 502 503 ecode = self.getPtraceEvent() >> 8 504 505 if tid == self.getPid(): 506 self._fireExit( ecode ) 507 self.platformDetach() 508 509 else: 510 self.detachThread(tid, ecode) 511 512 elif sig == SIG_LINUX_CLONE: 513 # Handle a new thread here! 514 newtid = self.getPtraceEvent() 515 #print('CLONE (new tid: %d)' % newtid) 516 self.attachThread(newtid, attached=True) 517 518 elif sig == signal.SIGSTOP and tid != self.pid: 519 #print('OMG IM THE NEW THREAD! %d' % tid) 520 # We're not even a real event right now... 521 self.runAgain() 522 self._stopped_cache[tid] = True 523 524 elif sig == signal.SIGSTOP: 525 # If we are still 'exec()'ing, we havent hit the SIGTRAP 526 # yet ( so our process info is still python, lets skip it ) 527 if self.execing: 528 self._stopped_hack = True 529 self.setupPtraceOptions(tid) 530 self.runAgain() 531 532 elif self._stopped_hack: 533 newtid = self.getPtraceEvent(tid) 534 #print("WHY DID WE GET *ANOTHER* STOP?: %d" % tid) 535 #print('PTRACE EVENT: %d' % newtid) 536 self.attachThread(newtid, attached=True) 537 538 else: # on first attach... 539 self._stopped_hack = True 540 self.setupPtraceOptions(tid) 541 self.handlePosixSignal(sig) 542 543 #FIXME eventually implement child catching! 544 else: 545 self.handlePosixSignal(sig) 546 547 return 548 549 v_posix.PosixMixin.platformProcessEvent(self, event)
550 551 @v_base.threadwrap
552 - def getPtraceEvent(self, tid=None):
553 """ 554 This *thread wrapped* function will get any pending GETEVENTMSG 555 msgs. 556 """ 557 p = c_ulong(0) 558 if tid == None: 559 tid = self.getMeta("ThreadId", -1) 560 if v_posix.ptrace(PT_GETEVENTMSG, tid, 0, addressof(p)) != 0: 561 raise Exception('ptrace PT_GETEVENTMSG failed!') 562 return p.value
563
564 - def platformGetThreads(self):
565 ret = {} 566 for tid in self.pthreads: 567 ret[tid] = tid #FIXME make this pthread struct or stackbase soon 568 return ret
569
570 - def platformGetMaps(self):
571 maps = [] 572 mapfile = file("/proc/%d/maps" % self.pid) 573 for line in mapfile: 574 575 perms = 0 576 sline = line.split(" ") 577 addrs = sline[0] 578 permstr = sline[1] 579 fname = sline[-1].strip() 580 addrs = addrs.split("-") 581 base = long(addrs[0],16) 582 max = long(addrs[1],16) 583 mlen = max-base 584 585 if "r" in permstr: 586 perms |= e_mem.MM_READ 587 if "w" in permstr: 588 perms |= e_mem.MM_WRITE 589 if "x" in permstr: 590 perms |= e_mem.MM_EXEC 591 #if "p" in permstr: 592 #pass 593 594 maps.append((base,mlen,perms,fname)) 595 return maps
596
597 - def platformGetFds(self):
598 fds = [] 599 for name in os.listdir("/proc/%d/fd/" % self.pid): 600 try: 601 fdnum = int(name) 602 fdtype = vtrace.FD_UNKNOWN 603 link = os.readlink("/proc/%d/fd/%s" % (self.pid,name)) 604 if "socket:" in link: 605 fdtype = vtrace.FD_SOCKET 606 elif "pipe:" in link: 607 fdtype = vtrace.FD_PIPE 608 elif "/" in link: 609 fdtype = vtrace.FD_FILE 610 611 fds.append((fdnum,fdtype,link)) 612 except: 613 traceback.print_exc() 614 615 return fds
616 617 ############################################################################ 618 # 619 # NOTE: Both of these use class locals set by the i386/amd64 variants 620 # 621 @v_base.threadwrap
622 - def platformGetRegCtx(self, tid):
623 ctx = self.archGetRegCtx() 624 u = self.user_reg_struct() 625 if v_posix.ptrace(PT_GETREGS, tid, 0, addressof(u)) == -1: 626 raise Exception("Error: ptrace(PT_GETREGS...) failed!") 627 628 ctx._rctx_Import(u) 629 return ctx
630 631 @v_base.threadwrap
632 - def platformSetRegCtx(self, tid, ctx):
633 u = self.user_reg_struct() 634 # Populate the reg struct with the current values (to allow for 635 # any regs in that struct that we don't track... *fs_base*ahem* 636 if v_posix.ptrace(PT_GETREGS, tid, 0, addressof(u)) == -1: 637 raise Exception("Error: ptrace(PT_GETREGS...) failed!") 638 639 ctx._rctx_Export(u) 640 if v_posix.ptrace(PT_SETREGS, tid, 0, addressof(u)) == -1: 641 raise Exception("Error: ptrace(PT_SETREGS...) failed!") 642 643 """ 644 for i in intel_dbgregs: 645 val = ctx.getRegister(self.dbgidx + i) 646 offset = self.user_dbg_offset + (self.psize * i) 647 if v_posix.ptrace(v_posix.PT_WRITE_U, tid, offset, val) != 0: 648 libc.perror('PT_WRITE_U failed for debug%d' % i) 649 #raise Exception("PT_WRITE_U for debug%d failed!" % i) 650 """
651
652 -class Linuxi386Trace( 653 vtrace.Trace, 654 LinuxMixin, 655 v_i386.i386Mixin, 656 v_posix.ElfMixin, 657 v_base.TracerBase):
658 659 user_reg_struct = user_regs_i386 660 user_dbg_offset = 252 661 reg_val_mask = 0xffffffff 662
663 - def __init__(self):
664 vtrace.Trace.__init__(self) 665 v_base.TracerBase.__init__(self) 666 v_posix.ElfMixin.__init__(self) 667 v_i386.i386Mixin.__init__(self) 668 LinuxMixin.__init__(self) 669 670 # Pre-calc the index of the debug regs 671 self.dbgidx = self.archGetRegCtx().getRegisterIndex("debug0")
672 673 @v_base.threadwrap
674 - def platformGetRegCtx(self, tid):
675 ctx = LinuxMixin.platformGetRegCtx( self, tid ) 676 for i in intel_dbgregs: 677 offset = self.user_dbg_offset + (self.psize * i) 678 r = v_posix.ptrace(v_posix.PT_READ_U, tid, offset, 0) 679 ctx.setRegister(self.dbgidx+i, r & self.reg_val_mask) 680 return ctx
681 682 @v_base.threadwrap
683 - def platformSetRegCtx(self, tid, ctx):
684 LinuxMixin.platformSetRegCtx( self, tid, ctx ) 685 for i in intel_dbgregs: 686 val = ctx.getRegister(self.dbgidx + i) 687 offset = self.user_dbg_offset + (self.psize * i) 688 if v_posix.ptrace(v_posix.PT_WRITE_U, tid, offset, val) != 0: 689 libc.perror('PT_WRITE_U failed for debug%d' % i)
690 691 @v_base.threadwrap
692 - def platformAllocateMemory(self, size, perms=e_mem.MM_RWX, suggestaddr=0):
693 sp = self.getStackCounter() 694 pc = self.getProgramCounter() 695 696 # Xlate perms (mmap is backward) 697 realperm = 0 698 if perms & e_mem.MM_READ: 699 realperm |= 1 700 if perms & e_mem.MM_WRITE: 701 realperm |= 2 702 if perms & e_mem.MM_EXEC: 703 realperm |= 4 704 705 #mma is struct of mmap args for linux syscall 706 mma = struct.pack("<6L", suggestaddr, size, realperm, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0) 707 708 regsave = self.getRegisters() 709 710 stacksave = self.readMemory(sp, len(mma)) 711 ipsave = self.readMemory(pc, 2) 712 713 SYS_mmap = 90 714 715 self.writeMemory(sp, mma) 716 self.writeMemory(pc, "\xcd\x80") 717 self.setRegisterByName("eax", SYS_mmap) 718 self.setRegisterByName("ebx", sp) 719 self._syncRegs() 720 721 try: 722 # Step over our syscall instruction 723 tid = self.getMeta("ThreadId", 0) 724 self.platformStepi() 725 os.waitpid(tid, 0) 726 eax = self.getRegisterByName("eax") 727 if eax & 0x80000000: 728 raise Exception("Linux mmap syscall error: %d" % eax) 729 return eax 730 731 finally: 732 # Clean up all our fux0ring 733 self.writeMemory(sp, stacksave) 734 self.writeMemory(pc, ipsave) 735 self.setRegisters(regsave)
736
737 738 -class LinuxAmd64Trace( 739 vtrace.Trace, 740 LinuxMixin, 741 v_amd64.Amd64Mixin, 742 v_posix.ElfMixin, 743 v_base.TracerBase):
744 745 user_reg_struct = user_regs_amd64 746 user_dbg_offset = 848 747 reg_val_mask = 0xffffffffffffffff 748
749 - def __init__(self):
750 vtrace.Trace.__init__(self) 751 v_base.TracerBase.__init__(self) 752 v_posix.ElfMixin.__init__(self) 753 v_amd64.Amd64Mixin.__init__(self) 754 LinuxMixin.__init__(self) 755 756 self.dbgidx = self.archGetRegCtx().getRegisterIndex("debug0")
757 758 @v_base.threadwrap
759 - def platformGetRegCtx(self, tid):
760 ctx = LinuxMixin.platformGetRegCtx( self, tid ) 761 for i in intel_dbgregs: 762 offset = self.user_dbg_offset + (self.psize * i) 763 r = v_posix.ptrace(v_posix.PT_READ_U, tid, offset, 0) 764 ctx.setRegister(self.dbgidx+i, r & self.reg_val_mask) 765 return ctx
766 767 @v_base.threadwrap
768 - def platformSetRegCtx(self, tid, ctx):
769 LinuxMixin.platformSetRegCtx( self, tid, ctx ) 770 for i in intel_dbgregs: 771 val = ctx.getRegister(self.dbgidx + i) 772 offset = self.user_dbg_offset + (self.psize * i) 773 if v_posix.ptrace(v_posix.PT_WRITE_U, tid, offset, val) != 0: 774 libc.perror('PT_WRITE_U failed for debug%d' % i)
775 776 arm_break_be = 'e7f001f0'.decode('hex') 777 arm_break_le = 'f001f0e7'.decode('hex')
778 779 -class LinuxArmTrace( 780 vtrace.Trace, 781 LinuxMixin, 782 v_arm.ArmMixin, 783 v_posix.ElfMixin, 784 v_base.TracerBase):
785 786 user_reg_struct = user_regs_arm 787 reg_val_mask = 0xffffffff 788
789 - def __init__(self):
790 vtrace.Trace.__init__(self) 791 v_base.TracerBase.__init__(self) 792 v_posix.ElfMixin.__init__(self) 793 v_arm.ArmMixin.__init__(self) 794 LinuxMixin.__init__(self) 795 796 self._break_after_bp = False 797 self._step_cleanup = []
798
799 - def _fireStep(self):
800 # See notes below about insanity... 801 if self._step_cleanup != None: 802 [ self.writeMemory( bva, bytes ) for (bva,bytes) in self._step_cleanup ] 803 self._step_cleanup = None 804 return v_base.TracerBase._fireStep( self )
805
806 - def archGetBreakInstr(self):
807 return arm_break_le
808 #return 'FIXME' 809 810 @v_base.threadwrap
811 - def platformStepi(self):
812 # This is a total rediculous hack to account 813 # for the fact that the arm platform couldn't 814 # be bothered to implement single stepping in 815 # the stupid hardware... 816 817 self.stepping = True 818 819 pc = self.getProgramCounter() 820 op = self.parseOpcode( pc ) 821 822 branches = op.getBranches( self ) 823 if not branches: 824 raise Exception(''' 825 The branches for the instruction %r were not decoded correctly. This means that 826 we cant properly predict the possible next instruction executions in a way that allows us 827 to account for the STUPID INSANE FACT THAT THERE IS NO HARDWARE SINGLE STEP CAPABILITY ON 828 ARM (non-realtime or JTAG anyway). We *would* have written invalid instructions to each 829 of those locations and cleaned them up before you ever knew anything was amiss... which is 830 how we pretend arm can single step... even though IT CANT. (please tell visi...) 831 ''' % op) 832 833 # Save the memory at the branches for later 834 # restoration in the _fireStep callback. 835 836 self._step_cleanup = [] 837 for bva,bflags in op.getBranches( self ): 838 self._step_cleanup.append( (bva, self.readMemory( bva, 4 )) ) 839 self.writeMemory( bva, arm_break_le ) 840 841 tid = self.getMeta('ThreadId') 842 843 if v_posix.ptrace(v_posix.PT_CONTINUE, tid, 0, 0) != 0: 844 raise Exception("ERROR ptrace failed for tid %d" % tid)
845