chiark / gitweb /
f77a4daa9e0d855983bc69cf55b31810288ae891
[chiark-utils.git] / fishdescriptor / greenend / fishdescriptor / indonor.py
1
2 # class for use inside gdb which is debugging the donor process
3
4 import gdb
5 import os
6
7 try:
8     rtld_now = os.RTLD_NOW
9 except AttributeError:
10     try:
11         import dl
12         rtld_now = dl.RTLD_NOW
13     except ImportError:
14         # some installations lack dl, it seems
15         # https://bugs.launchpad.net/ubuntu/+source/python2.7/+bug/1721840
16         # bodge:
17         rtld_now = 2
18
19 def _string_escape_for_c(s):
20     if not isinstance(s, bytes):
21         s = s.encode('utf-8') # sigh, python 2/3 compat
22     out = ''
23     for c in bytearray(s): # gets us bytes in py2 and py3
24         if c == ord('\\') or c == ord('=') or c < 32 or c > 126:
25             out += '\\x%02x' % c
26         else:
27             out += chr(c)
28     return out
29
30 class DonorImplementation():
31     def __init__(self):
32         # works on the current gdb.Inferior
33         # ideally should be reused if the same process is targetd
34         self._open = None
35         self._sym = None
36
37     def _func(self, functype, funcname, realargs):
38         expr = '((%s) %s) %s' % (functype, funcname, realargs)
39         return gdb.parse_and_eval(expr)
40
41     def _dlfunc(self, functype, funcname, realargs):
42         r = self._func(functype,funcname,realargs)
43         if not r:
44             err = self._func('char* (*)(void)', 'dlerror', '()')
45             if not err:
46                 err = 'dlerror said NULL!'
47             else:
48                 err = err.string()
49             raise RuntimeError("%s failed: %s" % (funcname, err))
50         return r
51
52     def _dlopen(self):
53         if self._open is not None: return
54         o = self._dlfunc('void* (*)(const char*, int)',
55                          'dlopen',
56                          '("libfishdescriptor-donate.so.1.0", %s)' % rtld_now)
57         self._open = '((void*)%s)' % o
58
59     def _dlsym(self):
60         if self._sym is not None: return
61         self._sym = self._dlfunc('void* (*)(void*, const char*)'
62                                  'dlsym',
63                                  '(%s, "fishdescriptor_donate")' % self._open)
64
65     def donate(self, path, fds):
66         self._dlopen()
67         self._dlsym()
68         r = self._func('int (*)(const char*, const int*)',
69                        self._sym,
70                        '("%s", (int[%d]){ %s, -1 })'
71                        % (_string_escape_for_c(path),
72                           len(fds) + 1,
73                           ', '.join(["%d" % fd for fd in fds])))
74         if r:
75             err = self._func('char* (*)(int)',
76                              strerror,
77                              r)
78             if not err:
79                 err = 'strerror failed'
80             else:
81                 err = err.string()
82             raise RuntimeError("donate failed: %s" % err)