btf/btfhive/aarch64/getFuncs.py (305 lines of code) (raw):
# -*- coding: utf-8 -*-
# cython:language_level=3
"""
-------------------------------------------------
File Name: getFuncs
Description :
Author : liaozhaoyan
date: 2021/12/1
-------------------------------------------------
Change Activity:
2021/12/1:
-------------------------------------------------
"""
__author__ = 'liaozhaoyan'
import sys
import os
import shlex
from subprocess import PIPE, Popen
import select
import sqlite3
import json
import re
ON_POSIX = 'posix' in sys.builtin_module_names
class CasyncCmdQue(object):
def __init__(self, cmd):
super(CasyncCmdQue, self).__init__()
self.daemon = True # thread dies with the program
self.__p = Popen(shlex.split(cmd), stdout=PIPE, stdin=PIPE, close_fds=ON_POSIX)
self.__e = select.epoll()
self.__e.register(self.__p.stdout.fileno(), select.EPOLLIN)
def __del__(self):
self.__p.kill()
def write(self, cmd):
try:
self.__p.stdin.write(cmd.encode())
self.__p.stdin.flush()
except IOError:
return -1
def writeLine(self, cmd):
self.write(cmd + "\n")
def read(self, tmout=0.2, l=16384):
while True:
es = self.__e.poll(tmout)
if not es:
return ""
for f, e in es:
if e & select.EPOLLIN:
s = os.read(f, l).decode()
return s
def readw(self, want, tries=100):
i = 0
r = ""
while i < tries:
line = self.read()
if want in line:
return r + line
r += line
i += 1
raise Exception("get want args %s overtimes" % want)
def terminate(self):
self.__p.terminate()
return self.__p.wait()
class CgetVminfo(object):
def __init__(self, vmPath):
super(CgetVminfo, self).__init__()
self._gdb = CasyncCmdQue("gdb %s" % vmPath)
self._gdb.readw("(gdb)", 500)
self._gdb.writeLine("set pagination off")
self._gdb.readw("(gdb)")
def genType(self, t):
self._gdb.writeLine(f"ptype {t}")
r = self._gdb.readw("(gdb)").split("\n")[0]
try:
_, alias = r.split("=", 1)
except ValueError:
return [None, None]
alias = alias.strip()
self._gdb.writeLine(f"p sizeof({t})")
r = self._gdb.readw("(gdb)").split("\n")[0]
_, size = r.split("=", 1)
size = int(size.strip())
return [alias, size]
def genFile(self, fName="funs.txt"):
self._gdb.writeLine("i functions")
with open(fName, 'w') as f:
s = "dummy"
while "\n(gdb)" not in s:
s = self._gdb.read(tmout=240)
f.write(s)
class CgenfuncsDb(object):
def __init__(self, dbName):
# self._txt = txt
self._db = None
self._vm = None
# dbName = self._parseName()
self._setupDb(dbName)
def __del__(self):
if self._db is not None:
self._db.commit()
self._db.close()
def _setupDb(self, dbName):
if os.path.exists(dbName):
os.remove(dbName)
self._db = sqlite3.connect(dbName)
cur = self._db.cursor()
sql = """CREATE TABLE files (
id INTEGER PRIMARY KEY autoincrement,
file TEXT
);"""
cur.execute(sql)
sql = """CREATE TABLE funs (
id INTEGER PRIMARY KEY autoincrement,
func VARCHAR (128),
args JSON,
ret VARCHAR (64),
line INTEGER,
fid INTEGER
);"""
cur.execute(sql)
sql = """CREATE TABLE structs (
id INTEGER PRIMARY KEY autoincrement,
name VARCHAR (64),
members INTEGER,
bytes INTEGER
);"""
cur.execute(sql)
sql = """CREATE TABLE members (
id INTEGER PRIMARY KEY autoincrement,
fid INTEGER,
types VARCHAR (128),
name VARCHAR (64),
offset INTEGER,
bytes INTEGER,
bits VARCHAR (16) DEFAULT ""
);"""
cur.execute(sql)
sql = """CREATE TABLE types (
id INTEGER PRIMARY KEY autoincrement,
name VARCHAR (64),
alias VARCHAR (64),
bytes INTEGER
);"""
cur.execute(sql)
cur.close()
def _arg_split(self, argStr):
args = []
arg = ""
count = 0
for a in argStr:
if count == 0 and a == ",":
args.append(arg.strip())
arg = ""
continue
elif a == "(":
count += 1
elif a == ")":
count -= 1
arg += a
if arg != "":
args.append(arg.strip())
return args
def funcs(self, funcPath):
cur = self._db.cursor()
with open(funcPath, 'r') as f:
fid = 0
for index, line in enumerate(f):
line = line[:-1]
if line == "":
continue
elif line.startswith("(gdb)"):
break
elif line.startswith("File "):
_, sFile = line.split(" ", 1)
sql = f'''INSERT INTO files (file) VALUES ("{sFile[:-1]}")'''
cur.execute(sql)
fid = cur.lastrowid
elif line.endswith(");"):
#8: static int __paravirt_pgd_alloc(struct mm_struct *);
line = line[:-2]
lineNo, body = line.split(":", 1)
head, args = body.split("(", 1)
# args = [x.strip() for x in args.split(",")]
args = self._arg_split(args)
if "*" in head:
ret, func = head.rsplit("*", 1)
ret += "*"
else:
ret, func = head.rsplit(" ", 1)
sql = f'''INSERT INTO funs (func, args, ret, line, fid) VALUES \
("{func}", '{json.dumps(args)}', "{ret.strip()}", {lineNo}, {fid})'''
cur.execute(sql)
cur.close()
def _insStructs(self, cur, lines):
name, _ = lines[0].rsplit(" ", 1)
ds = {"members": 0, "size":0}
for line in lines[::-1]:
if "/* size" in line:
""" /* size: 16, cachelines: 1, members: 2 */"""
l = line.strip()
beg = l.index("/*") + 2
end = l.index("*/")
l = l[beg:end]
vs = l.split(",")
for c in vs:
k, v = c.split(":")
ds[k.strip()] = v.strip()
sql = f'''INSERT INTO structs (name, members, bytes) VALUES \
("{name}", {ds["members"]}, {ds["size"]})'''
cur.execute(sql)
return cur.lastrowid
def _parseMember(self, cur, fid, line, pre=""):
"""struct list_head * next; /* 0 8 */"""
"""void (*func)(struct callback_head *); /* 8 8 */"""
"""unsigned int p:1; /* 4:15 4 */"""
if ";" not in line:
return
bits = ""
body, anno = line.split(";")
if "/*" not in anno:
return
if body[-1] == ")": #func
_, func = body.split("(*", 1)
func, _ = func.split(")", 1)
types = body.replace(f" (*{func})(", " (*)(", 1)
types = re.sub(" +", " ", types)
name = func
else:
types, name = body.rsplit(" ", 1)
types = types.strip()
name = name.strip()
if ":" in name:
name, bits = name.split(":", 1)
name = pre + name
beg = anno.index("/*") + 2
end = anno.index("*/")
l = anno[beg:end].strip()
offset, bytes = l.rsplit(" ", 1)
offset = offset.strip()
if ":" in offset:
offset, start = offset.split(":", 1)
bits = start.strip() + ":" + bits
sql = f'''INSERT INTO members (fid, types, name, offset, bytes, bits) VALUES \
({fid}, "{types}", "{name}", {offset.strip()}, {bytes.strip()}, "{bits}")'''
cur.execute(sql)
def _parseBox(self, cur, fid, lines, pre):
"""union {"""
"""} pci;"""
t = lines[0].split(" ", 1)[0]
if t in ["union", "struct"]:
lastLine = lines[-1]
if not lastLine.startswith("};"):
npre, _ = lastLine[1:].split(";", 1)
_, npre = npre.rsplit(" ", 1)
pre += npre.strip() + "."
self._parseLoop(cur, fid, lines, pre)
def _parseLoop(self, cur, fid, lines, pre):
qCount = 0
box = []
for line in lines[1:-1]:
line = line.strip()
if line.startswith("/* size:"):
break
lCount = line.count("{")
rCount = line.count("}")
qCount += lCount - rCount
if qCount > 0:
box.append(line)
elif len(box) > 0:
box.append(line)
self._parseBox(cur, fid, box, pre)
box = []
else:
self._parseMember(cur, fid, line, pre)
def _parseStruct(self, cur, lines):
fid = self._insStructs(cur, lines)
self._parseLoop(cur, fid, lines, "")
def structs(self, fName):
cur = self._db.cursor()
with open(fName, 'r') as f:
lines = []
for index, line in enumerate(f):
lines.append(line)
if line.startswith("}"):
self._parseStruct(cur, lines)
lines = []
cur.close()
def _save_type(self, cur, t):
alias, size = self._vm.genType(t)
if alias:
sql = f'INSERT INTO types (name, alias, bytes) VALUES ("{t}", "{alias}", {size})'
cur.execute(sql)
def _type_is_in(self, cur, t):
sql = f"SELECT name FROM types WHERE name = '{t}'"
res = cur.execute(sql)
if res is None:
return False
r = res.fetchone()
if r is None:
return False
return True
def _is_types(self, t):
if "*" in t:
return None
if t.startswith("const "):
t = t[6:]
if t.startswith("static "):
t = t[7:]
if t.startswith("volatile "):
t = t[8:]
if t.startswith("struct ") or t.startswith("union ") or t.startswith("enum "):
return None
if t in ("void", "..."):
return None
return t
def _check_type(self, cur, t):
t = self._is_types(t)
if t and not self._type_is_in(cur, t):
self._save_type(cur, t)
def _type(self, args, ret):
if args is not None:
args = json.loads(args)
else:
args = []
args.append(ret)
cur = self._db.cursor()
for arg in args:
self._check_type(cur, arg)
cur.close()
def types(self, vmlinux):
cur = self._db.cursor()
self._vm = CgetVminfo(vmlinux)
self._save_type(cur, "void *")
sql = "SELECT args, ret FROM funs"
res = cur.execute(sql)
if res is None:
return
r = res.fetchone()
while r is not None:
self._type(*r)
r = res.fetchone()
sql = "SELECT types FROM members"
res = cur.execute(sql)
if res is None:
return
r = res.fetchone()
while r is not None:
self._type(None, r[0])
r = res.fetchone()
cur.close()
if __name__ == "__main__":
d = CgenfuncsDb("info-4.19.91-25.al7.x86_64.db")
os.system("pahole /home/vmhive/vmlinux/alinux/vmlinux-4.19.91-25.al7.x86_64 > struct.txt")
d.structs("struct.txt")
d.funcs("/home/vmhive/funcs/alinux/funs-4.19.91-25.al7.x86_64.txt")
d.types("/home/vmhive/vmlinux/alinux/vmlinux-4.19.91-25.al7.x86_64")
pass