2 # class for use inside gdb which is debugging the donor process
6 def _string_escape_for_c(s):
7 if not isinstance(s, bytes):
8 s = s.encode('utf-8') # sigh, python 2/3 compat
10 for c in bytearray(s): # gets us bytes in py2 and py3
11 if c == ord('\\') or c == ord('=') or c < 32 or c > 126:
17 class DonorImplementation():
19 # works on the current gdb.Inferior
20 # ideally should be reused if the same process is targetd
24 def _func(self, functype, funcname, realargs):
25 expr = '((%s) %s) %s' % (functype, funcname, realargs)
26 return gdb.parse_and_eval(expr)
28 def _dlfunc(self, functype, funcname, realargs):
29 r = self._func(functype,funcname,realargs)
31 err = self._func('char* (*)(void)', 'dlerror', '()')
33 err = 'dlerror said NULL!'
36 raise RuntimeError("%s failed: %s" % (funcname, err))
40 if self._open is not None: return
41 rtld_print_cmd = ['fishdescriptor','--print-rtld-now'];
42 rtld_now = subprocess.check_output(rtld_print_cmd).rstrip('\n')
43 o = self._dlfunc('void* (*)(const char*, int)',
45 '("libfishdescriptor-donate.so.1.0", %s)' % rtld_now)
46 self._open = '((void*)%s)' % o
49 if self._sym is not None: return
50 self._sym = self._dlfunc('void* (*)(void*, const char*)'
52 '(%s, "fishdescriptor_donate")' % self._open)
54 def donate(self, path, fds):
57 r = self._func('int (*)(const char*, const int*)',
59 '("%s", (int[%d]){ %s, -1 })'
60 % (_string_escape_for_c(path),
62 ', '.join(["%d" % fd for fd in fds])))
64 err = self._func('char* (*)(int)',
68 err = 'strerror failed'
71 raise RuntimeError("donate failed: %s" % err)