--- /dev/null
+
+# class for use inside gdb which is debugging the donor process
+
+import gdb
+
+def _string_escape_for_c(s):
+ if not isinstance(s, bytes):
+ s = s.encode('utf-8') # sigh, python 2/3 compat
+ out = ''
+ for c in bytearray(s): # gets us bytes in py2 and py3
+ if c == ord('\\') or c == ord('=') or c < 32 or c > 126:
+ out += '\\x%02x' % c
+ else:
+ out += chr(c)
+ return out
+
+class DonorImplementation():
+ def __init__(self):
+ # works on the current gdb.Inferior
+ # ideally should be reused if the same process is targetd
+ self._open = None
+ self._sym = None
+
+ def _func(self, functype, funcname, realargs):
+ expr = '((%s) %s) %s' % (functype, funcname, realargs)
+ return gdb.parse_and_eval(expr)
+
+ def _dlfunc(self, functype, funcname, realargs):
+ r = self._func(functype,funcname,realargs)
+ if not r:
+ err = self._func('char* (*)(void)', 'dlerror', '()')
+ if not err:
+ err = 'dlerror said NULL!'
+ else:
+ err = err.string()
+ raise RuntimeError("%s failed: %s" % (funcname, err))
+ return r
+
+ def _dlopen(self):
+ if self._open is not None: return
+ rtld_print_cmd = ['fishdescriptor','--print-rtld-now'];
+ rtld_now = subprocess.check_output(rtld_print_cmd).rstrip('\n')
+ o = self._dlfunc('void* (*)(const char*, int)',
+ 'dlopen',
+ '("libfishdescriptor-donate.so.1.0", %s)' % rtld_now)
+ self._open = '((void*)%s)' % o
+
+ def _dlsym(self):
+ if self._sym is not None: return
+ self._sym = self._dlfunc('void* (*)(void*, const char*)'
+ 'dlsym',
+ '(%s, "fishdescriptor_donate")' % self._open)
+
+ def donate(self, path, fds):
+ self._dlopen()
+ self._dlsym()
+ r = self._func('int (*)(const char*, const int*)',
+ self._sym,
+ '("%s", (int[%d]){ %s, -1 })'
+ % (_string_escape_for_c(path),
+ len(fds) + 1,
+ ', '.join(["%d" % fd for fd in fds])))
+ if r:
+ err = self._func('char* (*)(int)',
+ strerror,
+ r)
+ if not err:
+ err = 'strerror failed'
+ else:
+ err = err.string()
+ raise RuntimeError("donate failed: %s" % err)