ref: 795ee8f8c9ef7eae69fdd2d00bfc4b7753e31dbb
dir: /support/syscall-gen/c2myr.py/
import sys
import re
import os
import traceback
import pycparser
import StringIO
from pycparser import c_parser, c_ast
from pycparser.plyparser import Coord
#from pycparser import c_parser, c_ast, parse_file
#from pycparser.plyparser import Coord
ctypes = {}
myrtypes = {
'void': 'void',
'char': 'byte',
'short': 'int16',
'int': 'int',
'long': 'int64',
'long long': 'int64',
'unsigned char': 'byte',
'unsigned short': 'uint16',
'unsigned': 'uint',
'unsigned int': 'uint',
'unsigned long': 'uint64',
'unsigned long long': 'uint64',
'unsigned short int': 'uint16',
'unsigned long int': 'uint64',
'unsigned long long int': 'uint64',
'short int': 'int16',
'long int': 'int64',
'long long int': 'int64',
'int8_t': 'byte',
'int16_t': 'int16',
'int32_t': 'int32',
'int64_t': 'int64',
'uint8_t': 'byte',
'uint16_t': 'uint16',
'uint32_t': 'uint32',
'uint64_t': 'uint64',
'size_t': 'size',
'ssize_t': 'size', # myrddin sizes are signed
# kernel-defined types?
'u_char': 'byte',
'u_short': 'uint16',
'u_int': 'uint',
'u_long': 'uint64',
'u_int8': 'uint8',
'u_int16': 'uint16',
'u_int32': 'uint32',
'u_int64': 'uint64',
'int8': 'int8',
'int16': 'int16',
'int32': 'int32',
'int64': 'int64',
'i8': 'int8',
'i16': 'int16',
'i32': 'int32',
'i64': 'int64',
'u8': 'uint8',
'u16': 'uint16',
'u32': 'uint32',
'u64': 'uint64',
'__int64_t': 'int64',
'char': 'byte',
'short': 'int16',
'int': 'int',
'long': 'int64',
'caddr_t': 'void#',
}
have = set()
#valid for all unixes we support...
sizes = {
'char': 1,
'short': 2,
'int': 4,
'long': 8,
'long int': 8,
'long long': 8,
'long long int': 8,
'unsigned char': 1,
'unsigned short': 2,
'unsigned short int': 2,
'unsigned int': 4,
'unsigned long': 8,
'unsigned long int': 8,
'unsigned long long': 8,
'unsigned long long int': 8,
'void*': 1,
}
def fullydefined(t):
return type(t) == c_ast.Struct and t.decls
def collectdef(n, t):
if t in [c_ast.Decl, c_ast.TypeDecl] and type(n.type) == c_ast.Struct:
if fullydefined(n.type):
ctypes[n.type.name] = n.type
elif t == c_ast.Typedef:
# typedef struct foo foo is valid.
if n.name not in ctypes:
ctypes[n.name] = n
# as is 'typedef struct foo { ... } bar'
if type(n.type) is c_ast.TypeDecl:
if type(n.type.type) is c_ast.Struct:
collectdef(n.type, type(n.type))
def collect(ast):
for n in ast.ext:
collectdef(n, type(n))
def typename(t):
if type(t) == c_ast.Struct:
return t.name
elif type(t) == c_ast.IdentifierType:
return ' '.join(t.names)
elif type(t) == c_ast.Typedef:
return t.name
elif type(t) == c_ast.Struct:
return t.name
elif type(t) == c_ast.TypeDecl:
return typename(t.type)
elif type(t) == c_ast.TypeDecl:
return typename(t.type)
else:
t.show()
raise ValueError('unknown type')
def deprefix(decls):
pfx = None
for d in decls:
name = d.name
if not name:
return
if name.startswith('__'):
name = name[2:]
sp = name.split('_', 1)
if len(sp) != 2:
return
if pfx and sp[0] != pfx:
return
pfx = sp[0]
for d in decls:
if d.name.startswith('__'):
d.name = d.name[2:]
(hd, tl) = d.name.split('_', 1)
d.name = tl
def myrname(n):
namemap = {
'acl_type_t': 'acltype',
'acl_tag_t': 'acltag',
'acl_perm_t': 'aclperm',
'acl_entry_type_t': 'aclenttype',
'mode_t': 'filemode',
'ssize_t': 'size',
'__socklen_t': 'int32',
'socklen_t': 'int32',
'fd_set': 'fdset',
'__ucontext': 'ucontext',
'cap_rights_t': 'caprights',
'stack_t': 'sigstack',
'thr_param': 'thrparam',
'stat': 'statbuf',
'nstat': 'statbuf',
'newstat': 'statbuf',
}
if n in namemap:
return namemap[n]
if n.endswith('_t'):
n = n[:-2]
if n.startswith('__'):
n = n[2:]
return n
def tysize(t):
if type(t) == c_ast.Typename:
return tysize(t.type)
if type(t) == c_ast.TypeDecl:
return tysize(t.type)
if type(t) == c_ast.Typedef:
return tysize(t.type)
if type(t) == c_ast.Typedef:
return tysize(t.type)
if type(t) == c_ast.PtrDecl:
return sizes['void*']
if type(t) == c_ast.ArrayDecl:
return tysize(t.type) * fold(t.dim)
elif type(t) == c_ast.IdentifierType:
name = ' '.join(t.names)
if name in ctypes:
return tysize(ctypes[name])
else:
return sizes[name]
else:
raise ValueError('unsupported type' + str(t))
def fold(expr):
try:
if type(expr) is c_ast.BinaryOp:
if expr.op == '+': return fold(expr.left) + fold(expr.right)
if expr.op == '-': return fold(expr.left) - fold(expr.right)
if expr.op == '*': return fold(expr.left) * fold(expr.right)
if expr.op == '/': return fold(expr.left) / fold(expr.right)
if expr.op == '%': return fold(expr.left) % fold(expr.right)
if expr.op == '&': return fold(expr.left) & fold(expr.right)
if expr.op == '|': return fold(expr.left) | fold(expr.right)
if expr.op == '^': return fold(expr.left) >> fold(expr.right)
if expr.op == '<<': return fold(expr.left) << fold(expr.right)
if expr.op == '>>': return fold(expr.left) >> fold(expr.right)
elif type(expr) is c_ast.UnaryOp:
if expr.op == '+': return fold(expr.expr)
if expr.op == '-': return -fold(expr.expr)
if expr.op == 'sizeof':
return tysize(expr.expr)
elif type(expr) is c_ast.Cast:
return fold(expr.expr)
else:
return int(expr.value)
except Exception:
raise
def emitstruct(t, defname):
fields = []
if t.name:
name = myrname(t.name)
else:
name = defname
deprefix(t.decls)
for d in t.decls:
if not d.name:
raise ValueError('null name in type')
etype = emit(d.type)
fname = d.name
if fname == 'type':
fname = 'kind'
fields.append('{}\t: {}'.format(fname, etype))
structs.write('type {} = struct\n\t{}\n\n;;\n\n'.format(name, '\n\t'.join(fields)))
return name
def emit(t, defname=None):
if type(t) == c_ast.Struct:
if t.name:
if myrname(t.name) in have:
return t.name
# linked lists can be a problem...
have.add(myrname(t.name))
if not t.decls:
t = ctypes[t.name]
return emitstruct(t, defname)
elif type(t) == c_ast.IdentifierType:
name = ' '.join(t.names)
if name in myrtypes:
return myrtypes[' '.join(t.names)]
return myrtype(name, defname)
elif type(t) == c_ast.Typedef:
if t.name in myrtypes:
return myrtypes[t.name]
name = myrname(t.name)
if name in have:
return name
# fold chains of typedefs
sub = emit(t.type, name)
if defname:
return myrname(sub)
else:
if name != sub and name not in have:
have.add(name)
defs.write('type {} = {}\n'.format(name, sub))
return name
elif type(t) == c_ast.TypeDecl:
return emit(t.type, defname)
elif type(t) == c_ast.ArrayDecl:
ret = emit(t.type)
return '{}[{}]'.format(ret, fold(t.dim))
elif type(t) == c_ast.PtrDecl:
# Myrddin functions aren't easy to translate to C.
if type(t.type) == c_ast.FuncDecl:
return 'void#'
return emit(t.type) + '#'
elif type(t) == c_ast.Union:
raise ValueError('unions unsupported')
else:
t.show()
raise ValueError('unknown type')
def myrtype(name, defname=None):
if name is None:
return None
if myrname(name) in myrtypes:
return myrtypes[myrname(name)]
t = ctypes[name]
gen = emit(t, defname)
have.add(myrname(gen))
return gen
if __name__ == '__main__':
structs = StringIO.StringIO()
defs = StringIO.StringIO()
prehdr = '''
#define __builtin_va_list int
#define __inline
#define __inline__
#define __restrict
#define __attribute__(_)
#define __asm(x)
#define __asm__(x)
#define __extension__
#define __signed__
#define __extern_inline__
#define volatile(_) /* catches '__asm volatile(asm)' on openbsd */
#include <sys/types.h>
#include <sys/cdefs.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#include <utime.h>
#include <ucontext.h>
#include <linux/futex.h>
#endif
'''
includes = []
# A bunch of these headers are system-only. We wouldn't
# care, but they also try to prevent people from depending
# on them accidentally. As a result, let's just try including
# each one ahead of time, and see if it works.
#
# if so, we generate a mega-header with all the types.
syshdr = []
if os.path.exists("/usr/include/sys"):
syshdr = ["sys/" + h for h in os.listdir('/usr/include/sys')]
if os.path.exists("/usr/include/linux"):
syshdr = syshdr + ["linux/" + h for h in os.listdir("/usr/include/linux")]
# it seems that sys headers can also be stashed here. Shit.
if os.path.exists("/usr/include/x86_64-linux-gnu/sys"):
syshdr = syshdr + ["linux/" + h for h in os.listdir("/usr/include/linux")]
for hdr in syshdr:
print 'trying {}'.format(hdr)
if not hdr.endswith(".h"):
continue
try:
include = prehdr + '\n#include <{}>'.format(hdr)
with open('/tmp/myr-includes.h', 'w') as f:
f.write(include)
pycparser.parse_file('/tmp/myr-includes.h', use_cpp=True)
includes.append('#include <{}>'.format(hdr))
except Exception:
print '...skip'
print('importing' + ' '.join(includes))
with open('/tmp/myr-includes.h', 'w') as f:
f.write(prehdr + '\n' + '\n'.join(includes))
with open('want.txt') as f:
want = f.read().split()
with open('have.txt') as f:
have |= set(f.read().split())
for sz in ['', 8, 16, 32, 64]:
have.add('int{}'.format(sz))
have.add('uint{}'.format(sz))
ast = pycparser.parse_file('/tmp/myr-includes.h', use_cpp=True)
collect(ast)
for w in want:
try:
myrtype(w)
except (ValueError,KeyError) as e:
print('unconvertable type {}: add a manual override: {}'.format(w, e))
f = 'gentypes+{}-{}.frag'.format(sys.argv[1], sys.argv[2])
with open(f, 'w') as f:
f.write(defs.getvalue())
f.write('\n')
f.write(structs.getvalue())