5c2772f59d
<sys/linux-syscalls.h> only contains constants for the syscalls we're generating stubs for. We want all the syscalls available on the architecture in question. Keep using <sys/linux-syscalls.h> on ARM for now because the __NR_ARM_set_tls and __NR_ARM_cacheflush values aren't in <asm/unistd.h>. Change-Id: I66683950d87d9b18d6107d0acc0ed238a4496f44
514 lines
16 KiB
Python
Executable File
514 lines
16 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# this tool is used to generate the syscall assembler templates
|
|
# to be placed into arch-{arm,x86,mips}/syscalls, as well as the content
|
|
# of arch-{arm,x86,mips}/linux/_syscalls.h
|
|
#
|
|
|
|
import sys, os.path, glob, re, commands, filecmp, shutil
|
|
import getpass
|
|
|
|
from bionic_utils import *
|
|
|
|
# get the root Bionic directory, simply this script's dirname
|
|
#
|
|
bionic_root = find_bionic_root()
|
|
if not bionic_root:
|
|
print "could not find the Bionic root directory. aborting"
|
|
sys.exit(1)
|
|
|
|
if bionic_root[-1] != '/':
|
|
bionic_root += "/"
|
|
|
|
print "bionic_root is %s" % bionic_root
|
|
|
|
# temp directory where we store all intermediate files
|
|
bionic_temp = "/tmp/bionic_gensyscalls/"
|
|
|
|
# all architectures, update as you see fit
|
|
all_archs = [ "arm", "mips", "x86" ]
|
|
|
|
def make_dir( path ):
|
|
path = os.path.abspath(path)
|
|
if not os.path.exists(path):
|
|
parent = os.path.dirname(path)
|
|
if parent:
|
|
make_dir(parent)
|
|
os.mkdir(path)
|
|
|
|
def create_file( relpath ):
|
|
dir = os.path.dirname( bionic_temp + relpath )
|
|
make_dir(dir)
|
|
return open( bionic_temp + relpath, "w" )
|
|
|
|
#
|
|
# x86 assembler templates for each syscall stub
|
|
#
|
|
|
|
x86_header = """/* autogenerated by gensyscalls.py */
|
|
#include <linux/err.h>
|
|
#include <machine/asm.h>
|
|
#include <asm/unistd.h>
|
|
|
|
ENTRY(%(fname)s)
|
|
"""
|
|
|
|
x86_registers = [ "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" ]
|
|
|
|
x86_call = """ movl $%(idname)s, %%eax
|
|
int $0x80
|
|
cmpl $-MAX_ERRNO, %%eax
|
|
jb 1f
|
|
negl %%eax
|
|
pushl %%eax
|
|
call __set_errno
|
|
addl $4, %%esp
|
|
orl $-1, %%eax
|
|
1:
|
|
"""
|
|
|
|
x86_return = """ ret
|
|
END(%(fname)s)
|
|
"""
|
|
|
|
#
|
|
# ARM assembler templates for each syscall stub
|
|
#
|
|
|
|
arm_header = """/* autogenerated by gensyscalls.py */
|
|
#include <linux/err.h>
|
|
#include <machine/asm.h>
|
|
#include <sys/linux-syscalls.h>
|
|
|
|
ENTRY(%(fname)s)
|
|
"""
|
|
|
|
arm_eabi_call_default = arm_header + """\
|
|
mov ip, r7
|
|
ldr r7, =%(idname)s
|
|
swi #0
|
|
mov r7, ip
|
|
cmn r0, #(MAX_ERRNO + 1)
|
|
bxls lr
|
|
neg r0, r0
|
|
b __set_errno
|
|
END(%(fname)s)
|
|
"""
|
|
|
|
arm_eabi_call_long = arm_header + """\
|
|
mov ip, sp
|
|
.save {r4, r5, r6, r7}
|
|
stmfd sp!, {r4, r5, r6, r7}
|
|
ldmfd ip, {r4, r5, r6}
|
|
ldr r7, =%(idname)s
|
|
swi #0
|
|
ldmfd sp!, {r4, r5, r6, r7}
|
|
cmn r0, #(MAX_ERRNO + 1)
|
|
bxls lr
|
|
neg r0, r0
|
|
b __set_errno
|
|
END(%(fname)s)
|
|
"""
|
|
|
|
#
|
|
# mips assembler templates for each syscall stub
|
|
#
|
|
|
|
mips_call = """/* autogenerated by gensyscalls.py */
|
|
#include <asm/unistd.h>
|
|
.text
|
|
.globl %(fname)s
|
|
.align 4
|
|
.ent %(fname)s
|
|
|
|
%(fname)s:
|
|
.set noreorder
|
|
.cpload $t9
|
|
li $v0, %(idname)s
|
|
syscall
|
|
bnez $a3, 1f
|
|
move $a0, $v0
|
|
j $ra
|
|
nop
|
|
1:
|
|
la $t9,__set_errno
|
|
j $t9
|
|
nop
|
|
.set reorder
|
|
.end %(fname)s
|
|
"""
|
|
|
|
def param_uses_64bits(param):
|
|
"""Returns True iff a syscall parameter description corresponds
|
|
to a 64-bit type."""
|
|
param = param.strip()
|
|
# First, check that the param type begins with one of the known
|
|
# 64-bit types.
|
|
if not ( \
|
|
param.startswith("int64_t") or param.startswith("uint64_t") or \
|
|
param.startswith("loff_t") or param.startswith("off64_t") or \
|
|
param.startswith("long long") or param.startswith("unsigned long long") or
|
|
param.startswith("signed long long") ):
|
|
return False
|
|
|
|
# Second, check that there is no pointer type here
|
|
if param.find("*") >= 0:
|
|
return False
|
|
|
|
# Ok
|
|
return True
|
|
|
|
def count_arm_param_registers(params):
|
|
"""This function is used to count the number of register used
|
|
to pass parameters when invoking an ARM system call.
|
|
This is because the ARM EABI mandates that 64-bit quantities
|
|
must be passed in an even+odd register pair. So, for example,
|
|
something like:
|
|
|
|
foo(int fd, off64_t pos)
|
|
|
|
would actually need 4 registers:
|
|
r0 -> int
|
|
r1 -> unused
|
|
r2-r3 -> pos
|
|
"""
|
|
count = 0
|
|
for param in params:
|
|
if param_uses_64bits(param):
|
|
if (count & 1) != 0:
|
|
count += 1
|
|
count += 2
|
|
else:
|
|
count += 1
|
|
return count
|
|
|
|
def count_generic_param_registers(params):
|
|
count = 0
|
|
for param in params:
|
|
if param_uses_64bits(param):
|
|
count += 2
|
|
else:
|
|
count += 1
|
|
return count
|
|
|
|
class State:
|
|
def __init__(self):
|
|
self.old_stubs = []
|
|
self.new_stubs = []
|
|
self.other_files = []
|
|
self.syscalls = []
|
|
|
|
def x86_genstub(self, fname, numparams, idname):
|
|
t = { "fname" : fname,
|
|
"idname" : idname }
|
|
|
|
result = x86_header % t
|
|
stack_bias = 4
|
|
for r in range(numparams):
|
|
result += " pushl " + x86_registers[r] + "\n"
|
|
stack_bias += 4
|
|
|
|
for r in range(numparams):
|
|
result += " mov %d(%%esp), %s" % (stack_bias+r*4, x86_registers[r]) + "\n"
|
|
|
|
result += x86_call % t
|
|
|
|
for r in range(numparams):
|
|
result += " popl " + x86_registers[numparams-r-1] + "\n"
|
|
|
|
result += x86_return % t
|
|
return result
|
|
|
|
def x86_genstub_cid(self, fname, numparams, idname, cid):
|
|
# We'll ignore numparams here because in reality, if there is a
|
|
# dispatch call (like a socketcall syscall) there are actually
|
|
# only 2 arguments to the syscall and 2 regs we have to save:
|
|
# %ebx <--- Argument 1 - The call id of the needed vectored
|
|
# syscall (socket, bind, recv, etc)
|
|
# %ecx <--- Argument 2 - Pointer to the rest of the arguments
|
|
# from the original function called (socket())
|
|
t = { "fname" : fname,
|
|
"idname" : idname }
|
|
|
|
result = x86_header % t
|
|
stack_bias = 4
|
|
|
|
# save the regs we need
|
|
result += " pushl %ebx" + "\n"
|
|
stack_bias += 4
|
|
result += " pushl %ecx" + "\n"
|
|
stack_bias += 4
|
|
|
|
# set the call id (%ebx)
|
|
result += " mov $%d, %%ebx" % (cid) + "\n"
|
|
|
|
# set the pointer to the rest of the args into %ecx
|
|
result += " mov %esp, %ecx" + "\n"
|
|
result += " addl $%d, %%ecx" % (stack_bias) + "\n"
|
|
|
|
# now do the syscall code itself
|
|
result += x86_call % t
|
|
|
|
# now restore the saved regs
|
|
result += " popl %ecx" + "\n"
|
|
result += " popl %ebx" + "\n"
|
|
|
|
# epilog
|
|
result += x86_return % t
|
|
return result
|
|
|
|
|
|
def arm_eabi_genstub(self,fname, flags, idname):
|
|
t = { "fname" : fname,
|
|
"idname" : idname }
|
|
if flags:
|
|
numargs = int(flags)
|
|
if numargs > 4:
|
|
return arm_eabi_call_long % t
|
|
return arm_eabi_call_default % t
|
|
|
|
|
|
def mips_genstub(self,fname, idname):
|
|
t = { "fname" : fname,
|
|
"idname" : idname }
|
|
return mips_call % t
|
|
|
|
def process_file(self,input):
|
|
parser = SysCallsTxtParser()
|
|
parser.parse_file(input)
|
|
self.syscalls = parser.syscalls
|
|
parser = None
|
|
|
|
for t in self.syscalls:
|
|
syscall_func = t["func"]
|
|
syscall_params = t["params"]
|
|
syscall_name = t["name"]
|
|
|
|
if t["common"] >= 0 or t["armid"] >= 0:
|
|
num_regs = count_arm_param_registers(syscall_params)
|
|
t["asm-arm"] = self.arm_eabi_genstub(syscall_func,num_regs,"__NR_"+syscall_name)
|
|
|
|
if t["common"] >= 0 or t["x86id"] >= 0:
|
|
num_regs = count_generic_param_registers(syscall_params)
|
|
if t["cid"] >= 0:
|
|
t["asm-x86"] = self.x86_genstub_cid(syscall_func, num_regs, "__NR_"+syscall_name, t["cid"])
|
|
else:
|
|
t["asm-x86"] = self.x86_genstub(syscall_func, num_regs, "__NR_"+syscall_name)
|
|
elif t["cid"] >= 0:
|
|
E("cid for dispatch syscalls is only supported for x86 in "
|
|
"'%s'" % syscall_name)
|
|
return
|
|
|
|
if t["common"] >= 0 or t["mipsid"] >= 0:
|
|
t["asm-mips"] = self.mips_genstub(syscall_func,"__NR_"+syscall_name)
|
|
|
|
|
|
def gen_NR_syscall(self, linux_fp, name, id):
|
|
linux_fp.write("#define __NR_%-25s (__NR_SYSCALL_BASE + %d)\n" % (name,id))
|
|
|
|
|
|
def scan_linux_unistd_h(self, fp, path):
|
|
pattern = re.compile(r'^#define __NR_([a-z]\S+) .*')
|
|
syscalls = set() # MIPS defines everything three times; work around that.
|
|
for line in open(path):
|
|
m = re.search(pattern, line)
|
|
if m:
|
|
syscalls.add(m.group(1))
|
|
for syscall in sorted(syscalls):
|
|
fp.write("#define SYS_%s __NR_%s\n" % (syscall, syscall))
|
|
|
|
|
|
def gen_linux_syscalls_h(self):
|
|
glibc_syscalls_h_path = "include/sys/glibc-syscalls.h"
|
|
glibc_fp = create_file(glibc_syscalls_h_path)
|
|
glibc_fp.write("/* Auto-generated by gensyscalls.py; do not edit. */\n")
|
|
glibc_fp.write("#ifndef _BIONIC_GLIBC_SYSCALLS_H_\n")
|
|
glibc_fp.write("#define _BIONIC_GLIBC_SYSCALLS_H_\n")
|
|
|
|
glibc_fp.write("#if defined(__arm__)\n")
|
|
self.scan_linux_unistd_h(glibc_fp, "libc/kernel/arch-arm/asm/unistd.h")
|
|
glibc_fp.write("#elif defined(__mips__)\n")
|
|
self.scan_linux_unistd_h(glibc_fp, "libc/kernel/arch-mips/asm/unistd.h")
|
|
glibc_fp.write("#elif defined(__i386__)\n")
|
|
self.scan_linux_unistd_h(glibc_fp, "libc/kernel/arch-x86/asm/unistd_32.h")
|
|
glibc_fp.write("#endif\n")
|
|
|
|
glibc_fp.write("#endif /* _BIONIC_GLIBC_SYSCALLS_H_ */\n")
|
|
glibc_fp.close()
|
|
self.other_files.append(glibc_syscalls_h_path)
|
|
|
|
linux_syscalls_h_path = "include/sys/linux-syscalls.h"
|
|
D("generating " + linux_syscalls_h_path)
|
|
fp = create_file(linux_syscalls_h_path)
|
|
fp.write( "/* Auto-generated by gensyscalls.py; do not edit. */\n" )
|
|
fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n" )
|
|
fp.write( "#define _BIONIC_LINUX_SYSCALLS_H_\n\n" )
|
|
fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H && !defined __ASM_MIPS_UNISTD_H\n" )
|
|
fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" )
|
|
fp.write( " # define __NR_SYSCALL_BASE 0x900000\n" )
|
|
fp.write( "#elif defined(__mips__)\n" )
|
|
fp.write( " # define __NR_SYSCALL_BASE 4000\n" )
|
|
fp.write( "#else\n" )
|
|
fp.write( " # define __NR_SYSCALL_BASE 0\n" )
|
|
fp.write( "#endif\n\n" )
|
|
|
|
# first, all common syscalls
|
|
for sc in sorted(self.syscalls,key=lambda x:x["common"]):
|
|
sc_id = sc["common"]
|
|
sc_name = sc["name"]
|
|
if sc_id >= 0:
|
|
self.gen_NR_syscall(fp, sc_name, sc_id)
|
|
|
|
# now, all arm-specific syscalls
|
|
fp.write("\n#ifdef __arm__\n")
|
|
for sc in self.syscalls:
|
|
sc_id = sc["armid"]
|
|
sc_name = sc["name"]
|
|
if sc_id >= 0:
|
|
self.gen_NR_syscall(fp, sc_name, sc_id)
|
|
fp.write("#endif\n")
|
|
|
|
gen_syscalls = {}
|
|
# all i386-specific syscalls
|
|
fp.write("\n#ifdef __i386__\n")
|
|
for sc in sorted(self.syscalls,key=lambda x:x["x86id"]):
|
|
sc_id = sc["x86id"]
|
|
sc_name = sc["name"]
|
|
if sc_id >= 0 and sc_name not in gen_syscalls:
|
|
self.gen_NR_syscall(fp, sc_name, sc_id)
|
|
gen_syscalls[sc_name] = True
|
|
fp.write("#endif\n")
|
|
|
|
# all mips-specific syscalls
|
|
fp.write("\n#ifdef __mips__\n")
|
|
for sc in sorted(self.syscalls,key=lambda x:x["mipsid"]):
|
|
sc_id = sc["mipsid"]
|
|
if sc_id >= 0:
|
|
self.gen_NR_syscall(fp, sc["name"], sc_id)
|
|
fp.write( "#endif\n" );
|
|
|
|
fp.write( "\n#endif\n" )
|
|
fp.write( "\n#endif /* _BIONIC_LINUX_SYSCALLS_H_ */\n" );
|
|
fp.close()
|
|
|
|
self.other_files.append(linux_syscalls_h_path)
|
|
|
|
|
|
# now dump the contents of syscalls.mk
|
|
def gen_arch_syscalls_mk(self, arch):
|
|
path = "arch-%s/syscalls.mk" % arch
|
|
D( "generating "+path )
|
|
fp = create_file( path )
|
|
fp.write( "# auto-generated by gensyscalls.py, do not touch\n" )
|
|
fp.write( "syscall_src := \n" )
|
|
arch_test = {
|
|
"arm": lambda x: x.has_key("asm-arm"),
|
|
"x86": lambda x: x.has_key("asm-x86"),
|
|
"mips": lambda x: x.has_key("asm-mips")
|
|
}
|
|
|
|
for sc in self.syscalls:
|
|
if arch_test[arch](sc):
|
|
fp.write("syscall_src += arch-%s/syscalls/%s.S\n" %
|
|
(arch, sc["func"]))
|
|
fp.close()
|
|
self.other_files.append( path )
|
|
|
|
|
|
# now generate each syscall stub
|
|
def gen_syscall_stubs(self):
|
|
for sc in self.syscalls:
|
|
if sc.has_key("asm-arm") and 'arm' in all_archs:
|
|
fname = "arch-arm/syscalls/%s.S" % sc["func"]
|
|
D2( ">>> generating "+fname )
|
|
fp = create_file( fname )
|
|
fp.write(sc["asm-arm"])
|
|
fp.close()
|
|
self.new_stubs.append( fname )
|
|
|
|
if sc.has_key("asm-x86") and 'x86' in all_archs:
|
|
fname = "arch-x86/syscalls/%s.S" % sc["func"]
|
|
D2( ">>> generating "+fname )
|
|
fp = create_file( fname )
|
|
fp.write(sc["asm-x86"])
|
|
fp.close()
|
|
self.new_stubs.append( fname )
|
|
|
|
if sc.has_key("asm-mips") and 'mips' in all_archs:
|
|
fname = "arch-mips/syscalls/%s.S" % sc["func"]
|
|
D2( ">>> generating "+fname )
|
|
fp = create_file( fname )
|
|
fp.write(sc["asm-mips"])
|
|
fp.close()
|
|
self.new_stubs.append( fname )
|
|
|
|
def regenerate(self):
|
|
D( "scanning for existing architecture-specific stub files" )
|
|
|
|
bionic_root_len = len(bionic_root)
|
|
|
|
for arch in all_archs:
|
|
arch_path = bionic_root + "arch-" + arch
|
|
D( "scanning " + arch_path )
|
|
files = glob.glob( arch_path + "/syscalls/*.S" )
|
|
for f in files:
|
|
self.old_stubs.append( f[bionic_root_len:] )
|
|
|
|
D( "found %d stub files" % len(self.old_stubs) )
|
|
|
|
if not os.path.exists( bionic_temp ):
|
|
D( "creating %s" % bionic_temp )
|
|
make_dir( bionic_temp )
|
|
|
|
D( "re-generating stubs and support files" )
|
|
|
|
self.gen_linux_syscalls_h()
|
|
for arch in all_archs:
|
|
self.gen_arch_syscalls_mk(arch)
|
|
self.gen_syscall_stubs()
|
|
|
|
D( "comparing files" )
|
|
adds = []
|
|
edits = []
|
|
|
|
for stub in self.new_stubs + self.other_files:
|
|
if not os.path.exists( bionic_root + stub ):
|
|
# new file, git add it
|
|
D( "new file: " + stub)
|
|
adds.append( bionic_root + stub )
|
|
shutil.copyfile( bionic_temp + stub, bionic_root + stub )
|
|
|
|
elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ):
|
|
D( "changed file: " + stub)
|
|
edits.append( stub )
|
|
|
|
deletes = []
|
|
for stub in self.old_stubs:
|
|
if not stub in self.new_stubs:
|
|
D( "deleted file: " + stub)
|
|
deletes.append( bionic_root + stub )
|
|
|
|
|
|
if adds:
|
|
commands.getoutput("git add " + " ".join(adds))
|
|
if deletes:
|
|
commands.getoutput("git rm " + " ".join(deletes))
|
|
if edits:
|
|
for file in edits:
|
|
shutil.copyfile( bionic_temp + file, bionic_root + file )
|
|
commands.getoutput("git add " +
|
|
" ".join((bionic_root + file) for file in edits))
|
|
|
|
commands.getoutput("git add %s%s" % (bionic_root,"SYSCALLS.TXT"))
|
|
|
|
if (not adds) and (not deletes) and (not edits):
|
|
D("no changes detected!")
|
|
else:
|
|
D("ready to go!!")
|
|
|
|
D_setlevel(1)
|
|
|
|
state = State()
|
|
state.process_file(bionic_root+"SYSCALLS.TXT")
|
|
state.regenerate()
|