Browse Source

add a wrapper for libctf..

main
John-Mark Gurney 1 month ago
parent
commit
5d42fec4e3
1 changed files with 400 additions and 0 deletions
  1. +400
    -0
      ctf.py

+ 400
- 0
ctf.py View File

@@ -0,0 +1,400 @@
#!/usr/bin/env python
#
# Copyright 2014 John-Mark Gurney.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# $Id$
#

import struct
import weakref

from ctypes import CDLL, Structure, POINTER, pointer, create_string_buffer, sizeof, CFUNCTYPE
from ctypes import c_int, c_uint, c_char_p, c_long, c_ulong, c_size_t, c_ssize_t, c_void_p

__all__ = [ 'CTF' ]

_ctflib = CDLL('libctf.so.2')
#_ctflib = CDLL('/usr/obj/arm.armeb/usr/src.avila/tmp/usr/src.avila/cddl/lib/libctf/libctf.so.2')

class CTFError(Exception):
pass

class HexInt(long):
def __str__(self):
return '%#x' % self

__repr__ = __str__

models = {
'CTF_MODEL_ILP32': 1,
'CTF_MODEL_LP64': 2,
}
models.update((v, k) for k, v in models.items())

# From: sys/ctf.h
kinds = {
'CTF_K_UNKNOWN': 0,
'CTF_K_INTEGER': 1,
'CTF_K_FLOAT': 2,
'CTF_K_POINTER': 3,
'CTF_K_ARRAY': 4,
'CTF_K_FUNCTION': 5,
'CTF_K_STRUCT': 6,
'CTF_K_UNION': 7,
'CTF_K_ENUM': 8,
'CTF_K_FORWARD': 9,
'CTF_K_TYPEDEF': 10,
'CTF_K_VOLATILE': 11,
'CTF_K_CONST': 12,
'CTF_K_RESTRICT': 13,
}
kinds.update((v, k) for k, v in kinds.items())

intenc = {
'CTF_INT_SIGNED': 0x01,
'CTF_INT_CHAR': 0x02,
'CTF_INT_BOOL': 0x04,
'CTF_INT_VARARGS': 0x08,
}
intenc.update((v, k) for k, v in intenc.items())

def lookupid(v, d):
tv = d[v]
try:
return int(v)
except ValueError:
return tv

def lookupstr(v, d):
tv = d[v]
if isinstance(tv, str):
return tv
else:
return d[tv]

class ctf_visit_f(Structure):
pass

class ctf_encoding_t(Structure):
_fields_ = [ ('cte_format', c_uint),
('cte_offset', c_uint),
('cte_bits', c_uint),
]

ctf_id_t = c_long

# If c_int != 0, return immediately
ctf_member_f = CFUNCTYPE(c_int, c_char_p, ctf_id_t, c_ulong, c_void_p)

class ctf_file_t(Structure):
pass

ctf_file_t_p = POINTER(ctf_file_t)

for r, f, a in (
(ctf_file_t_p, 'ctf_open', (c_char_p, POINTER(c_int), )),
(None, 'ctf_close', (ctf_file_t_p, )),
(c_int, 'ctf_update', (ctf_file_t_p, )),
(c_int, 'ctf_discard', (ctf_file_t_p, )),
(c_int, 'ctf_write', (ctf_file_t_p, c_int, )),

(ctf_file_t_p, 'ctf_parent_file', (ctf_file_t_p, )),
(c_char_p, 'ctf_parent_name', (ctf_file_t_p, )),

(c_int, 'ctf_setmodel', (ctf_file_t_p, c_int, )),
(c_int, 'ctf_getmodel', (ctf_file_t_p, )),

(c_int, 'ctf_errno', (ctf_file_t_p, )),
(c_char_p, 'ctf_errmsg', (c_int, )),
(c_int, 'ctf_version', (c_int, )),

(ctf_id_t, 'ctf_lookup_by_name', (ctf_file_t_p, c_char_p, )),
(ctf_id_t, 'ctf_lookup_by_symbol', (ctf_file_t_p, c_ulong, )),

(ctf_id_t, 'ctf_type_resolve', (ctf_file_t_p, ctf_id_t, )),
(c_ssize_t, 'ctf_type_lname', (ctf_file_t_p, ctf_id_t, c_char_p, c_size_t, )),
(c_char_p, 'ctf_type_name', (ctf_file_t_p, ctf_id_t, c_char_p, c_size_t, )),
(c_ssize_t, 'ctf_type_size', (ctf_file_t_p, ctf_id_t, )),
(c_ssize_t, 'ctf_type_align', (ctf_file_t_p, ctf_id_t, )),
(int, 'ctf_type_kind', (ctf_file_t_p, ctf_id_t, )),
(ctf_id_t, 'ctf_type_reference', (ctf_file_t_p, ctf_id_t, )),
(ctf_id_t, 'ctf_type_pointer', (ctf_file_t_p, ctf_id_t, )),
(int, 'ctf_type_encoding', (ctf_file_t_p, ctf_id_t, POINTER(ctf_encoding_t), )),
(int, 'ctf_type_visit', (ctf_file_t_p, ctf_id_t, POINTER(ctf_visit_f), c_void_p, )),
(int, 'ctf_type_cmp', (ctf_file_t_p, ctf_id_t, ctf_file_t_p, ctf_id_t, )),
(int, 'ctf_type_compat', (ctf_file_t_p, ctf_id_t, ctf_file_t_p, ctf_id_t, )),
):
fun = getattr(_ctflib, f)
setattr(fun, 'restype', r)
setattr(fun, 'argtypes', a)

class CTFID:
def __init__(self, _ctfobj, id):
self._ctfobj = _ctfobj
self._ctf = _ctfobj._ctf
self._id = id

__int_sizes = {
1: 'B',
2: 'H',
4: 'I',
8: 'Q',
}

def __call__(self, s, endian='@'):
size = self.getsize()

if len(s) < size:
raise ValueError('not enough bytes, needed %d', size)

base = self.resolve()
kind = base.getkind()

src, rem = s[:size], s[size:]

conv = endian
if kind == 'CTF_K_INTEGER':
enc = base.getencoding()
if enc.cte_format & intenc['CTF_INT_CHAR']:
assert size == 1
val = src
else:
convfun = lambda x: x
if enc.cte_format & intenc['CTF_INT_SIGNED']:
convfun = lambda x: x.lower()

if enc.cte_format & intenc['CTF_INT_BOOL']:
convchar = '?'
else:
convchar = self.__int_sizes[size]

conv += convfun(convchar)
val = struct.unpack(conv, src)[0]
elif kind == 'CTF_K_POINTER':
val = HexInt(struct.unpack(endian + self.__int_sizes[size], src)[0])
elif kind == 'CTF_K_ENUM':
raise NotImplementedError(kind)
elif kind in ('CTF_K_STRUCT', 'CTF_K_UNION'):
members = []
def addmember(*args):
members.append(args)
return 0

self.memberiter(addmember)

#print `kind`
#print `members`
val = {}
for name, _type, off in members:
if off % 8 != 0:
raise NotImplementedError('bytes only for now')
off /= 8
#print 'h:', `src`, `off`, `_type.getsize()`
val[name], trem = _type(src[off:off + _type.getsize()], endian)
assert not trem, 'trem left? %s' % `trem`
elif kind == 'CTF_K_UNION':
raise NotImplementedError(kind)
else:
raise NotImplementedError(kind)

return val, rem

def memberiter(self, fun):
iterfun = self._ctfobj.makeiterfun(fun)
_ctflib.ctf_member_iter(self._ctf, self._id, iterfun, None)

def resolve(self):
return self._ctfobj.resolve(self._id)

def reference(self):
return self._ctfobj.reference(self._id)

def getname(self):
buf = create_string_buffer(64)
rbuf = _ctflib.ctf_type_name(self._ctf, self._id, buf, sizeof(buf))
return rbuf

def getalign(self):
return _ctflib.ctf_type_align(self._ctf, self._id)

def getsize(self):
return _ctflib.ctf_type_size(self._ctf, self._id)

def getkind(self):
return lookupstr(_ctflib.ctf_type_kind(self._ctf, self._id),
kinds)

def getencoding(self):
enc = ctf_encoding_t()
r = _ctflib.ctf_type_encoding(self._ctf, self._id, pointer(enc))
if r != 0:
raise self._ctfobj.geterror()

return enc

def __repr__(self):
i = map(lambda x: ': '.join(x), [
('name', str(self.getname())),
('size', str(self.getsize())),
('kind', self.getkind()),
('align', str(self.getalign())),
])
return '<CTFID: %s>' % ', '.join(i)

class CTF:
def __init__(self, f, model=None):
i = c_int()
self._ctf = _ctflib.ctf_open(f, pointer(i))
if not bool(self._ctf):
raise ValueError('%s: %s' % (`f`,
_ctflib.ctf_errmsg(i)))

self._ids = weakref.WeakValueDictionary()

if model is not None:
_ctflib.ctf_setmodel(self._ctf, lookupid(model, models))

def makeiterfun(self, fun):
def lfun(name, id, off, ptr):
id = self._getid(id)
return fun(name, id, off)

return ctf_member_f(lfun)

def resolve(self, id):
if isinstance(id, CTFID):
id = id._id

nid = _ctflib.ctf_type_resolve(self._ctf, id)
return self._getid(nid)

def reference(self, id):
if isinstance(id, CTFID):
id = id._id

nid = _ctflib.ctf_type_reference(self._ctf, id)
return self._getid(nid)

def geterror(self):
err = _ctflib.ctf_errno(self._ctf)
errmsg = _ctflib.ctf_errmsg(err)
return CTFError((err, errmsg))

def __getitem__(self, k):
if isinstance(k, str):
id = _ctflib.ctf_lookup_by_name(self._ctf, k)
else:
id = _ctflib.ctf_lookup_by_symbol(self._ctf, k)

if id == -1:
raise KeyError('unknown name: %s' % `k`)
#print 'getitem:', `id`
return self._getid(id)

def _getid(self, id):
try:
return self._ids[id]
except KeyError:
obj = CTFID(self, id)
self._ids[id] = obj

return obj

def __del__(self):
try:
_ctflib.ctf_close(self._ctf)
except Exception:
pass

def dump(name, _type, off):
_type = _type.resolve()
print 'd:', `name, _type, off`
return 0

def parseddbw(s):
listofchars = [ x.split(':', 1)[1].split() for x in s.split('\n') ]

words = sum(listofchars, [])
return struct.pack('>%dI' % len(words), *(int(x, 16) for x in words))

def parseddb(s):
listofchars = [ x.split(':', 1)[1].split() for x in s.split('\n') ]

chars = sum(listofchars, [])
#print chars
l = ''.join(chr(int(x, 16)) for x in chars)

return l

if __name__ == '__main__':
#ctf = CTF('/boot/kernel/kernel')
ctf = CTF('/tmp/avila3.ctf', model='CTF_MODEL_ILP32')
#ctf = CTF('/tftpboot/kernel.avila.avila')

i = ctf['int']
vm = ctf['vm_page_t']
vmr = vm.resolve()
vmrr = vmr.reference()
print 'int:', `i`
print 'vm_page_t:', `vm`
print 'vm_page_t res:', `vmr`
print 'vm_page_t resolve:', `vmrr`
#vmrr.memberiter(dump)

print 'sizeof(struct vm_page):', ctf['struct vm_page'].getsize()
#print `vmrr('\x11' * vmrr.getsize())`
r = parseddb('''0xc0805db0: 0 0 0 0 c0 6d 1f 64 c0 80 5e 0 c0 6d 18 a0
0xc0805dc0: c0 6d 18 78 0 0 0 0 0 0 0 0 0 0 d f4
0xc0805dd0: 1 f 40 0 0 0 0 0 0 0 0 0 c0 df 40 0
0xc0805de0: 0 0 0 0 c0 80 5d e0 0 0 0 1 0 0 0 1
0xc0805df0: 0 0 0 0 0 4 ff 1 9 0 0 ff ff 0 0 0
0xc0805e00: 0 0 0 0 c0 6d 1f 4c c0 80 5e 50 c0 80 5d b8
0xc0805e10: c0 6d 18 78 0 0 0 0 0 0 0 0 0 0 d f5
0xc0805e20: 1 f 50 0 0 0 0 0 0 0 0 0 c0 df 50 0''')
r2 = parseddbw('''3229638064: 0 c06d1f64 c0805e00 c06d18a0
3229638080: c06d1878 0 0 df4
3229638096: 10f4000 0 0 c0df4000
3229638112: 0 c0805de0 1 1
3229638128: 0 4ff01 90000ff ff000000''')
r2 = parseddbw('''0xc07e73b0: 0 c06e6a4c c07e7400 c06e63a0
0xc07e73c0: c06e6378 0 0 dfd
0xc07e73d0: 8fd000 0 0 c0dfd000
0xc07e73e0: 0 c07e73e0 1 1
0xc07e73f0: 0 4ff02 90000ff ff000000
0xc07e7400: 0 c06e6a58 c07e7450 c07e73b8''')
print len(r), `r`
import pprint
s, r = vmrr(r, '>')
pprint.pprint(s)
s, r = vmrr(r2, '>')
pprint.pprint(s)

vm = ctf['vm_object_t'].resolve()
print `vm`
print 'sizeof(struct md_page):', ctf['struct md_page'].getsize()
print 'sizeof(vm_object_t):', ctf['vm_object_t'].getsize()
print 'sizeof(vm_pindex_t):', ctf['vm_pindex_t'].getsize()
print 'sizeof(vm_paddr_t):', ctf['vm_paddr_t'].getsize()

Loading…
Cancel
Save