-
Notifications
You must be signed in to change notification settings - Fork 115
/
make_ffi.py
executable file
·149 lines (126 loc) · 5.02 KB
/
make_ffi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import logging
import os
import platform
import re
import subprocess
import sys
import cffi
log = logging.getLogger("cffier")
log.setLevel(logging.DEBUG)
def find_good_scan(questionable):
known_good = []
end_line = len(questionable)
while len(questionable):
ffi = cffi.FFI()
log.debug("scan - trying %d good and %d questionable", len(known_good), len(questionable))
candidate = known_good + questionable[:end_line]
failed_line = -1
try:
ffi.cdef("\n".join(candidate))
known_good = candidate
questionable = questionable[end_line:]
end_line = len(questionable)
except AssertionError:
questionable = questionable[1:]
end_line = len(questionable)
except cffi.CDefError as e:
if "<cdef source string>" in str(e):
failed_line = int(str(e).split("\n")[-1].split(":")[1]) - 1
elif str(e).count(":") >= 2:
failed_line = int(str(e).split("\n")[1].split(":")[1])
failed_line_description = str(e).split("\n")[0]
idx1 = failed_line_description.index('"')
idx2 = failed_line_description.rindex('"')
failed_reason = failed_line_description[idx1 + 1 : idx2]
for i in range(failed_line, -1, -1):
if failed_reason in candidate[i]:
failed_line = i
elif "unrecognized construct" in str(e):
failed_line = int(str(e).split()[1][:-1]) - 1
elif "end of input" in str(e):
end_line -= 1
else:
raise Exception("Unknown error")
except cffi.FFIError as e:
if str(e).count(":") >= 2:
failed_line = int(str(e).split("\n")[0].split(":")[1]) - 1
else:
raise Exception("Unknown error")
if failed_line != -1:
end_line = failed_line - len(known_good)
if end_line == 0:
questionable = questionable[1:]
end_line = len(questionable)
return known_good
def doit(vex_path):
cpplist = ["cl", "cpp"]
cpp = os.getenv("CPP")
if cpp:
cpplist.insert(0, cpp)
if platform.system() == "Darwin":
cpplist.insert(0, "clang")
errs = []
for cpp in cpplist:
cmd = [cpp, "-I" + vex_path, os.path.join("pyvex_c", "pyvex.h")]
if cpp in ("cl", "clang", "gcc", "cc", "clang++", "g++"):
cmd.append("-E")
try:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
header, stderr = p.communicate()
try:
header = header.decode("utf-8")
stderr = stderr.decode("utf-8")
except UnicodeDecodeError:
# They don't have to be unicode on Windows
pass
if not header.strip() or p.returncode != 0:
errs.append((" ".join(cmd), p.returncode, stderr))
continue
else:
break
except OSError:
errs.append((" ".join(cmd), -1, "does not exist"))
continue
else:
log.warning("failed commands:\n" + "\n".join("{} ({}) -- {}".format(*e) for e in errs))
raise Exception(
"Couldn't process pyvex headers."
+ 'Please set CPP environmental variable to local path of "cpp".'
+ 'Note that "cpp" and "g++" are different.'
)
# header = vex_pp + pyvex_pp
linesep = "\r\n" if "\r\n" in header else "\n"
ffi_text = linesep.join(
line
for line in header.split(linesep)
if "#" not in line and line.strip() != "" and "jmp_buf" not in line and not ("=" in line and ";" in line)
)
ffi_text = re.sub(r"\{\s*\} NoOp;", "{ int DONOTUSE; } NoOp;", ffi_text)
ffi_text = re.sub(r"__attribute__\s*\(.*\)", "", ffi_text)
ffi_text = re.sub(r"__declspec\s*\([^\)]*\)", "", ffi_text)
ffi_text = ffi_text.replace("__const", "const")
ffi_text = ffi_text.replace("__inline", "")
ffi_text = ffi_text.replace("__w64", "")
ffi_text = ffi_text.replace("__cdecl", "")
ffi_text = ffi_text.replace("__int64", "long")
ffi_lines = ffi_text.split(linesep)
good = find_good_scan(ffi_lines)
good += ["extern VexControl vex_control;"]
with open("pyvex/vex_ffi.py", "w") as fp:
fp.write('ffi_str = """' + "\n".join(good) + '"""\n')
fp.write("guest_offsets = " + repr(get_guest_offsets(vex_path)) + "\n")
def get_guest_offsets(vex_path):
fname = os.path.join(vex_path, "libvex_guest_offsets.h")
out = {}
with open(fname) as fp:
for line in fp:
if line.startswith("#define"):
_, names, val = line.split()
val = int(val, 0)
assert names.startswith("OFFSET_")
_, arch, reg = names.split("_", 2)
out[(arch, reg.lower())] = val
return out
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
doit(sys.argv[1])